【问题标题】:React: make a cycle-through component with both forward and backward directions using GeneratorReact:使用 Generator 制作具有正向和反向方向的循环组件
【发布时间】:2021-11-24 00:09:54
【问题描述】:

我有一个字符串数组要显示const array = ["one", "two", "three"];

UI 最初显示数组中的第一项,即"one"。从那里我有一个按钮right,点击它会显示下一个项目或字符串two,然后是three,在three之后它应该回到one并从那里重新开始。 我还有一个left按钮,点击时显示上一个项目或字符串,如果当前字符串是two,前一个字符串是one,然后在one之后它从three开始并走向后。

我正在使用生成器来执行此操作。这是我的尝试


function* stepGen(steps) {
  let index = 0;
  while (true) {
    const direction = yield steps[index];
    index = (index + (direction === "forward" ? 1 : -1)) % steps.length;
  }
}

const array = ["one", "two", "three"];
let gen = stepGen(array);
const getNext = () => gen.next("forward").value;
const getPrev = () => gen.next("backward").value;

export default function App() {
  const [current, setCurrent] = useState(() => getNext());
  const onRight = () => {
    const next = getNext();
    setCurrent(next);
  };
  const onLeft = () => {
    const prev = getPrev();
    setCurrent(prev);
  };

  return (
    <div className="App">
      <h1>{current}</h1>
      <button onClick={onLeft}>left</button>
      <button onClick={onRight}>right</button>
    </div>
  );
}


这是一个你可以玩的现场演示 https://codesandbox.io/s/cyclethrough1-deh8p?file=/src/App.js

显然当前的行为是错误的。有多个问题我不知道原因和解决方法:

  1. 用户界面以two 开头,而不是one。我想这与我如何启动我的状态有关current
const [current, setCurrent] = useState(() => getNext());

我认为() =&gt; getNext() 只会在组件首次挂载时被调用一次,所以current 从一开始就应该是one

我试图用

启动状态
const [current, setCurrent] = useState(array[0]);

它确实从数组中的第一项开始,即one,但您必须单击两次right 按钮才能转到two。这是此变体的现场演示https://codesandbox.io/s/cyclethrough2-5gews?file=/src/App.js

  1. left 按钮,应该向后走循环不起作用。它完全坏掉了。 right 按钮虽然有效。不知道为什么。

【问题讨论】:

    标签: javascript reactjs ecmascript-6 es6-generator


    【解决方案1】:

    getPrev 的问题是 remainder (%) operator,它与模运算不同,当余数为负时返回负结果。要解决这个问题,请改用模函数:

    // modulo function
    const mod = (n, r) => ((n % r) + r) % r;
    

    为了解决第一次渲染的问题,在组件之外创建初始值。这是一种解决方法,因为我找不到该错误的原因。

    const init = getNext(); // get the initial value
    
    export default function App() {
      const [current, setCurrent] = useState(init); // use init value
    

    我还可以通过分别在getNextgetPrev 中传递1-1 来省去三元组来确定增量的需要。

    完整代码示例(sandbox):

    // modulo function
    const mod = (n, r) => ((n % r) + r) % r;
    
    function* stepGen(steps) {
      let index = 0;
      while (true) {
        const dir = yield steps[index];
        index = mod(index + dir, steps.length); // use mod function instead of remainder operator
      }
    }
    
    const array = ['one', 'two', 'three'];
    const gen = stepGen(array);
    
    const getPrev = () => gen.next(-1).value; // dec directly
    const getNext = () => gen.next(1).value; // inc directly
    
    const init = getNext(); // get the initial value
    
    export default function App() {
      const [current, setCurrent] = useState(init); // use init value
    
      const onLeft = () => {
        const next = getPrev();
        setCurrent(next);
      };
    
      const onRight = () => {
        const prev = getNext();
        setCurrent(prev);
      };
    
      return (
        <div className="App">
          <h1>{current}</h1>
          <button onClick={onLeft}>left</button>
          <button onClick={onRight}>right</button>
        </div>
      );
    }
    

    【讨论】:

      【解决方案2】:

      问题确实出在余数 (%) 运算符上,改用 const mod = (n, r) =&gt; ((n % r) + r) % r; 可以解决问题。

      我想回答我自己的问题,因为我想我了解导致初始状态出现奇怪问题的原因。

      1. 对于 UI 以二非一开头的问题,是 React 的严格模式的双重渲染造成的。
      2. 如果我们改用const [current, setCurrent] = useState(array[0]);,就会出现另一个问题,您需要双击right 使其转到two。这是因为在生成器中,index 从 0 开始,所以第一次点击后,迭代器会产生当前的index,即0 而不是1,因此我们需要双击它向前移动。

      【讨论】:

        【解决方案3】:

        所以,我建议的答案是:

        1. 首先,正如其中一个答案所提到的,它是由双重渲染引起的。我的建议是不要初始化 useState 中的值,而是使用空依赖项初始化 useEffect 钩子中的值。这将导致它在第一次渲染时只运行一次。示例:
          useEffect(() => {
            setCurrent(() => getNext())
          }, [])
        
        
        1. 该错误是由于您从索引中减去 1 造成的。当它位于位置 0 时,它会导致索引为 -1,这不会产生项目。一个简单的解决方案是,如果索引为负数,则将索引循环回最后一个元素。
        function* stepGen(steps) {
          let index = 0;
          while (true) {
            const direction = yield steps[index];
            index = (index + (direction === "forward" ? 1 : -1)) % steps.length;
            if (index < 0) {
              index = steps.length - 1;
            }
          }
        
        

        【讨论】:

        • 嘿,谢谢你的回答。有趣的是,setState 中的回调将在严格模式下被双重调用,但useEffect 中的回调不会?不知道。
        • @Joji 没错,useEffect 在依赖数组中的项目更新时被调用。如果您将空数组作为依赖项传递,它将仅在初始渲染时运行。我觉得这是对dmitripavlutin.com/react-useeffect-explanation的一个相当全面的解释
        【解决方案4】:

        使用像window.log()这样的js日志记录很明显,该错误是在单击左键后生成的数字是-2 -1,它们不是数组索引,所以一个想法可以是制作它们肯定的。但第一步是找到错误

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-29
          • 2019-08-10
          • 1970-01-01
          • 2017-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多