I am writing a util function called cycleThrough
that uses a generator to cycle through an iterable and can either go forward or backward. (I am really bad at naming please feel free to suggest a better name)
function cycleThrough(iterable, { startIndex = 0 } = {}) {
let index = startIndex;
const mod = (n, r) => ((n % r) r) % r;
const iterator = (function* () {
while (true) {
const dir = yield iterable[index];
index = mod(index dir, iterable.length);
}
})();
return {
getPrev: () => iterator.next(-1).value,
getNext: () => iterator.next(1).value
};
}
So if we pass const array = ["1", "2", "3"];
as the iterable
, then getNext
would iterate through the array one by one and go back to the beginning i.e. 1
-> 2
-> 3
-> 1
-> 2
, vice versa for getPrev
- 1
-> 3
-> 2
-> 1
In a React component I want to display the current item of the array, and one button to go to the next item and another button to go back to the previous item. This is my implementation.
function cycleThrough(iterable, { startIndex = 0 } = {}) {
let index = startIndex;
const mod = (n, r) => ((n % r) r) % r;
const iterator = (function* () {
while (true) {
const dir = yield iterable[index];
index = mod(index dir, iterable.length);
}
})();
return {
getPrev: () => iterator.next(-1).value,
getNext: () => iterator.next(1).value
};
}
const array = ["1", "2", "3"];
const { getNext, getPrev } = cycleThrough(array, { startIndex: 1 });
export default function App() {
const [current, setCurrent] = useState(array[0]);
const onLeft = () => {
const prev = getPrev();
setCurrent(prev);
};
const onRight = () => {
const next = getNext();
setCurrent(next);
};
return (
<div className="App">
<h1>{current}</h1>
<button onClick={onLeft}>left</button>
<button onClick={onRight}>right</button>
</div>
);
}
It works ok and here is the live demo you can play with
CodePudding user response:
As Drew mentioned, you can use useMemo to fix this. But I suggested another method. Using hooks. This is simpler and more appropriate to React.
const useCycleThrough = (iterable, startIndex) => {
const [index, setIndex] = useState(startIndex);
const goPrev = () => {
setIndex((old) => (iterable.length old - 1) % iterable.length);
};
const goNext = () => {
setIndex((old) => (iterable.length old 1) % iterable.length);
};
return [iterable[index], goPrev, goNext];
};