Реагировать: создать сквозной компонент с прямым и обратным направлениями с помощью генератора.
У меня есть массив строк для отображения
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
Ученик, текущее поведение глючит. Есть несколько проблем, причины и решения которых я не знаю:
- пользовательский интерфейс начинается с
two
нет . Я предполагаю, что это как-то связано с тем, как я инициирую свое состояние статистики
const [current, setCurrent] = useState(() => getNext());
я думал
() => getNext()
вызывается только один раз, когда компонент сначала монтируется, поэтому
current
должно быть
one
с самого начала.
- в
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>
);
}
Итак, мой предлагаемый ответ:
- Во-первых, как упоминалось в одном из ответов, это вызвано двойным рендерингом. Мое предложение - не инициализировать значение в
useState
но вuseEffect
ловушка с пустыми зависимостями. Это заставит его запускаться только один раз при первом рендеринге. Пример:
useEffect(() => {
setCurrent(() => getNext())
}, [])
- Ошибка вызвана тем, что вы вычитаете 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;
}
}
Проблема действительно была в операторе остатка (%), и используйте
const mod = (n, r) => ((n % r) + r) % r;
вместо этого исправил бы это.
Я хотел ответить на свои вопросы, потому что думаю, что понимаю, что вызывало странные проблемы с начальным состоянием.
- Проблема с пользовательским интерфейсом, начинающимся с двух, а не с одного, была вызвана двойным рендерингом строгого режима React.
- А затем, если мы используем
const [current, setCurrent] = useState(array[0]);
вместо этого может возникнуть другая проблема, когда вам нужно дважды щелкнутьright
заставить его пойти вtwo
. Это потому, что в генераторе начинается с 0, поэтому после первого щелчка итератор выдаст текущийindex
который0
нет1
, поэтому нам нужно дважды щелкнуть по нему, чтобы двигаться вперед.