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