Реагировать: создать сквозной компонент с прямым и обратным направлениями с помощью генератора.

У меня есть массив строк для отображения const array = ["one", "two", "three"]; .

Пользовательский интерфейс изначально показывает первый элемент в массиве, т.е. "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 onLeft = () => {
    const next = getNext();
    setCurrent(next);
  };
  const onRight = () => {
    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/frosty-rhodes-deh8p?file=/src/App.js

Ученик, текущее поведение глючит. Есть несколько проблем, причины и решения которых я не знаю:

  1. пользовательский интерфейс начинается с twoнет . Я предполагаю, что это как-то связано с тем, как я инициирую свое состояние статистики
      const [current, setCurrent] = useState(() => getNext());

я думал () => getNext() вызывается только один раз, когда компонент сначала монтируется, поэтому current должно быть one с самого начала.

  1. в rightкнопка, которая должна пересылать цикл, не работает. Он полностью сломан. в leftкнопка работает хотя. Не знаю почему.

4 ответа

Проблема с остатком ( %) , который, в отличие от операции по модулю, возвращает отрицательный результат, если остаток отрицательный. Чтобы решить эту проблему, используйте вместо этого функцию по модулю:

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

Чтобы решить проблему на 1-м рендере, создайте начальное значение вне компонента. Это обходной путь, поскольку я не могу найти причину этой ошибки.

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

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

Я бы также избавился от необходимости тернарного метода, чтобы определять приращение, передавая 1 а также -1 в getNext а также getPrev соответственно.

Пример полного кода (песочница ):

      // 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>
  );
}

Итак, мой предлагаемый ответ:

  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;
    }
  }

Используйте ведение журнала js, например window.log (). Хорошо видно, что ошибка возникает после щелчка влево, сгенерированные числа -2-1, которые не являются индексами массива, поэтому одна идея может заключаться в том, чтобы сделать их положительными. Но первый шаг найти ошибку

Проблема действительно была в операторе остатка (%), и используйте const mod = (n, r) => ((n % r) + r) % r; вместо этого исправил бы это.

Я хотел ответить на свои вопросы, потому что думаю, что понимаю, что вызывало странные проблемы с начальным состоянием.

  1. Проблема с пользовательским интерфейсом, начинающимся с двух, а не с одного, была вызвана двойным рендерингом строгого режима React.
  2. А затем, если мы используем const [current, setCurrent] = useState(array[0]); вместо этого может возникнуть другая проблема, когда вам нужно дважды щелкнуть right заставить его пойти в two. Это потому, что в генераторе начинается с 0, поэтому после первого щелчка итератор выдаст текущий index который 0 нет 1, поэтому нам нужно дважды щелкнуть по нему, чтобы двигаться вперед.
Другие вопросы по тегам