React:使用 Generator 制作一个具有向前和向后方向的循环组件

Joj*_*oji 6 javascript ecmascript-6 reactjs es6-generator

我有一个字符串数组要显示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>
  );
}


Run Code Online (Sandbox Code Playgroud)

这是一个现场演示,您可以使用 https://codesandbox.io/s/cyclethrough1-deh8p?file=/src/App.js

显然当前的行为是有问题的。有很多问题不知道原因和解决方案:

  1. UI 以twonotone开头。我想这与我如何启动状态有关current
const [current, setCurrent] = useState(() => getNext());
Run Code Online (Sandbox Code Playgroud)

我认为() => getNext()只有在组件首次安装时才会被调用一次,所以current应该one从一开始就调用。

我尝试启动状态

const [current, setCurrent] = useState(array[0]);
Run Code Online (Sandbox Code Playgroud)

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

  1. 应该向后走循环的按钮left不起作用。它完全坏了。right不过按钮可以用。不知道为什么。

Ori*_*ori 1

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

// modulo function
const mod = (n, r) => ((n % r) + r) % r;
Run Code Online (Sandbox Code Playgroud)

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

const init = getNext(); // get the initial value

export default function App() {
  const [current, setCurrent] = useState(init); // use init value
Run Code Online (Sandbox Code Playgroud)

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

完整代码示例(沙箱):

// 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>
  );
}
Run Code Online (Sandbox Code Playgroud)