I have an array of strings to display const array = ["one", "two", "three"];
.
The UI initially shows the first item in the array i.e. "one"
. From there I have a button right
when clicked it shows the next item or string which is two
, and then three
, after three
it should go back to one
and start from there again.
I also have a left
button, when clicked it shows the previous item or string, if the current string is two
, the previous string is one
, and then after one
it starts from three
and walks backward.
I am using generator to do it. Here is my attempt
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>
);
}
Here is a live demo you can play with https://codesandbox.io/s/cyclethrough1-deh8p?file=/src/App.js
Apparently the current behavior is buggy. There are multiple issues that I don't know the causes and the solutions:
- the UI starts with
two
notone
. I guess it has something to do with how I initiate my statecurrent
const [current, setCurrent] = useState(() => getNext());
I thought () => getNext()
is only to get called once when the component first mounts so current
should be one
from the start.
And I tried to initiated the state with
const [current, setCurrent] = useState(array[0]);
It indeed starts with the first item in the array which is one
but you have to click right
button twice to make it go to two
. Here is the live demo for this variation https://codesandbox.io/s/cyclethrough2-5gews?file=/src/App.js
- the
left
button, which should walk backward the loop doesn't work. It is broken completely. theright
button works though. Not sure why.
The problem with
getPrev
is the remainder (%
) operator, which unlike the modulo operation returns a negative result when the remainder is negative. To solve that use a modulo function instead:To solve the problem on the 1st render create the initial value outside of the component. This is a workaround, since I can't find the reason for that bug.
I would also save the need for a ternary to determine the increment by passing
1
and-1
ingetNext
andgetPrev
respectively.Full code example (sandbox):