Простое решение с использованием стиля обратного вызова установщика useState
Как указал @devserkan. Мы можем использовать предыдущее состояние, передав обратный вызов в setState
. Благодаря этому трюку мы избегаем зависимости от состояния.
const [pictureNumber, setPictureNumber] = useState(1);
useEffect(() => {
console.log("use effect called");
const handleUserKeyPress = (e) => {
if (e.code === "ArrowLeft") {
setPictureNumber(prev => prev - 1);
}
if (e.code === "ArrowRight") {
setPictureNumber(prev => prev + 1);
}
};
window.addEventListener("keydown", handleUserKeyPress);
return () => {
window.removeEventListener("keydown", handleUserKeyPress);
};
}, [setPictureNumber]);
Вот официальная документация: https://reactjs.org/docs/hooks-reference.html#functional -updates
Вот ручка, показывающая это решение: https://codepen.io/abumalick/pen/BaKaPgr
Решение с использованием ссылки
Это решение немного сложнее и будет полезно, когда вам нужно изменить функцию обработчика. Мы используем ссылку, useEffect
сохраняет ссылку на ссылку, которая не изменяется, и мы можем изменить свойство current
, которое изменяется.
useEffect
будет вызываться только один раз.
const [pictureNumber, setPictureNumber] = useState(1);
const callbackRef = useRef(null);
callbackRef.current = useCallback(
(e) => {
if (e.code === "ArrowLeft") {
setPictureNumber(pictureNumber - 1);
}
if (e.code === "ArrowRight") {
setPictureNumber(pictureNumber + 1);
}
},
[pictureNumber, setPictureNumber]
);
useEffect(() => {
const handleUserKeyPress = (e) => {
callbackRef.current(e);
};
console.log("use effect called");
window.addEventListener("keydown", handleUserKeyPress);
return () => {
window.removeEventListener("keydown", handleUserKeyPress);
};
}, [callbackRef]);
Вот кодовая ссылка: https://codepen.io/abumalick/pen/jOqOpLz