The issue is that func2
is defined when the component is loaded/updated, and since state updates are asynchronous, then when you're adding the event listener, it sets the currently defined func2
with the original idx
as the callback.
What you can do is to wrap func2 in a function that takes idx
as a parameter and returns a function that can then act as the callback.
Then you add the listener in setState
's callback to use the new state value right away.
const { useState } = React
const T = () => {
const [idx, setIdx] = useState(0);
const func1 = (e, c) => {
setIdx(prev => {
let newIdx = 1; // 1 instead of c for the example
window.addEventListener("mousemove", func2(newIdx)); // call outer func2 with new state
return newIdx // set new state
});
}
const func2 = idx => (e) => {
console.log(idx);
}
return <div id="drag-me" onClick={func1}>Click Here</div>
}
ReactDOM.render(
<T />,
window.root
)
#drag-me{
width: 100px;
height: 100px;
background-color: beige;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can also use useEffect
to add the event listener, but it'll run each time idx
is set, including when the component first mounts. You can use states to control this. For example, I used a state that represents func1
being called.
const { useState, useEffect } = React
const T = () => {
const [idx, setIdx] = useState(0);
const [funcCalled, setFuncCalled] = useState(false);
useEffect(()=> {
if(!funcCalled) return
const func2 = e => console.log(idx);
window.addEventListener('mousemove', func2);
return () =>{
if (!funcCalled) return
window.removeEventListener('mousemove', func2)
} // Cleanup after component unmounts
},[funcCalled])
const func1 = (e, c) => {
setIdx(1) // 1 instead of c for the example
setFuncCalled(true) // React respects state update order
}
return <div id="drag-me" onClick={func1}>Click Here</div>
}
ReactDOM.render(
<T />,
window.root
)
#drag-me{
width: 100px;
height: 100px;
background-color: beige;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Don't worry about the order of state updates, react will update idx
first becfore updating funcCalled
. Source