Reputation: 99
I have a React function component running along side a legacy JQuery application. When calling an event handler on a JQuery element, passing in the current React state defaults to the initial state value and not the updated state value.
Verified x state is changing through a useEffect hook, but when calling the event listener, x is set to the initial state value and not the state value after the update.
function MyComponent(props) {
const [x, setX] = useState(false);
// On initial render
useEffect(() => {
props.jQueryButton.addEventListener('click', onXSave)
}, [])
useEffect(() => {
console.log("x changed " + x); // everytime onXChange is called, x
state is updated with the checked value, and this console.log shows
the correct state value
}, [x]);
onXChange = event => {
setX(event.target.checked); // checked is true in this context
};
onXSave = event => {
const obj = { x: x}; // x is false here, even though the state in this context shows it to be true.
};
}
No error messages are displayed. In the above code in this context I expect the x state to be true in the onXSave method call, but it keeps displaying as false.
Upvotes: 3
Views: 1265
Reputation: 9787
The version of onXSave
that you're adding to your eventListener
is out of date - you only add it once on the first render, so when x
updates and causes a re-render, your useEffect doesn't run again and your jQueryButton
will still holding on to the original function, which has closed over an out of date version of x
.
You need to do two things:
onXSave
and your jQueryButton
as dependencies in your useEffect
dependency array (so when it re-renders, the current version of your function is hooked up to your eventListener)useEffect
.So something like:
function MyComponent(props) {
const [x, setX] = useState(false);
// On initial render
useEffect(() => {
props.jQueryButton.addEventListener('click', onXSave)
return ()=> props.jQueryButton.removeEventListener('click', onXSave)
}, [onXSave, props.jQueryButton])
useEffect(() => {
console.log("x changed " + x); // everytime onXChange is called, x
state is updated with the checked value, and this console.log shows
the correct state value
}, [x]);
onXChange = event => {
setX(event.target.checked); // checked is true in this context
};
onXSave = event => {
const obj = { x: x}; // x is false here, even though the state in this context shows it to be true.
};
}
Upvotes: 1
Reputation: 11600
onXSave
is added as a handler on initial render so x
has the value from that time. It doesn't matter that onXSave
is recreated on each render since it's never used past initial render.
To fix this you might put x
into a ref
unction MyComponent(props) {
const [x, setX] = useState(false);
const ref = useRef();
ref.current = x;
// On initial render
useEffect(() => {
props.jQueryButton.addEventListener('click', onXSave)
}, [])
useEffect(() => {
console.log("x changed " + x); // everytime onXChange is called, x
state is updated with the checked value, and this console.log shows
the correct state value
}, [x]);
onXChange = event => {
setX(event.target.checked); // checked is true in this context
};
onXSave = event => {
const obj = { x: ref.current}; // by using ref, the value is always current
};
}
Upvotes: 1