Callback function props.updateOrder() supposed to update props.index. In following code-block i've used this function but it dosent update the index. In the same component when using it in a onclick function it does. What am it doing wrong here?
const [image, setImage] = useState(polygons.polygonDefaultRight);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("keyup", handleKeyUp);
};
}, []);
const handleKeyUp = (e) => {
const keys = {
39: () => {
e.preventDefault();
setImage(polygons.polygonDefaultRight);
props.updateOrder(props.orders, props.index, 'next');
console.log('Right', props.index);
}
};
if (keys[e.keyCode]) {
keys[e.keyCode]();
}
};
const handleKeyDown = (e) => {
const keys = {
39: () => {
e.preventDefault();
setImage(polygons.polygonHoverRight);
}
};
if (keys[e.keyCode]) {
keys[e.keyCode]();
}
};
By only adding listeners on mount you create a closure over all the variables used in the handler function. For the setState() calls this can be worked around by passing a callback setState(prev => mutate(prev)), but the rest are stuck.
You can avoid this by simply adding relevant dependencies to the useEffect dependency array which will cleanup and then re-attach the listeners with updated handler functions.
In the below snippet the right arrow displays the closure over the initial state value, while the left arrow doesn't.
const { useState, useEffect } = React;
function App() {
const [ state, setState ] = useState(0);
// Closure
useEffect(() => {
document.addEventListener("keyup", handleKeyUpClosure);
return () => {
document.removeEventListener("keyup", handleKeyUpClosure);
};
}, []);
const handleKeyUpClosure = (e) => {
if (e.keyCode === 39) {
setState(s => s + 1);
console.clear();
console.log('Closure: ', state); // always logs 0
}
};
// No closure
useEffect(() => {
document.addEventListener("keyup", handleKeyUp);
return () => {
document.removeEventListener("keyup", handleKeyUp);
};
}, [ state ]);
const handleKeyUp = (e) => {
if (e.keyCode === 37) {
setState(s => s - 1);
console.clear();
console.log('No closure: ', state); // logs state of current render cycle (before update).
}
};
return (
<div>
<p>State: {state}</p>
<p>press right arrow to increment and left arrow to decrement.</p>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id='root'></div>