So I have this bit of code that does not work as expected. Current focus has been set using useState() on the parent component, hence its a state variable. However, when the currentFocus value changes in the parent, focus variable here itself is not updated. I would have expected the re render of the parent component, which in turn rerenders this component would cause the foucs value to change.
import React, { useRef, useEffect, useState } from 'react';
const CookieDetails = props => {
const {
name,
cost,
value,
numOwned,
onMouseClick,
cookieId,
currentFocus,
} = props;
let cookieInfoRef = useRef(null);
//My focus doesnt change even if I change currentFocus in parent component
const [focus, setFocus] = useState(currentFocus);
useEffect(() => {
console.log('currentFocus', currentFocus);
console.log('focus', focus);
console.log('cookieID', cookieId);
if (cookieInfoRef.current && cookieId === focus) {
console.log('current', cookieInfoRef.current);
cookieInfoRef.current.focus();
}
}, [focus]);
return (
<React.Fragment>
<button
onClick={onMouseClick}
ref={cookieId === focus ? cookieInfoRef : null}
>
<h3>{name}</h3>
<p>Cost:{cost}</p>
<p>Value:{value}</p>
<p>Owned:{numOwned}</p>
</button>
</React.Fragment>
);
};
export default CookieDetails;
Now I can solve this problem by doing the following instead,
import React, { useRef, useEffect, useState } from 'react';
const CookieDetails = props => {
const {
name,
cost,
value,
numOwned,
onMouseClick,
cookieId,
currentFocus,
} = props;
let cookieInfoRef = useRef(null);
useEffect(() => {
console.log('currentFocus', currentFocus);
console.log('cookieID', cookieId);
if (cookieInfoRef.current && cookieId === currentFocus) {
console.log('current', cookieInfoRef.current);
cookieInfoRef.current.focus();
}
});
return (
<React.Fragment>
<button
onClick={onMouseClick}
ref={cookieId === currentFocus ? cookieInfoRef : null}
>
<h3>{name}</h3>
<p>Cost:{cost}</p>
<p>Value:{value}</p>
<p>Owned:{numOwned}</p>
</button>
</React.Fragment>
);
};
export default CookieDetails;
But I only wanted to run the useEffect hook when focus/currentFocus is updated and not after every render. So why does this happen? What am I am missing to understand here.
Also I previously noticed if you did something like this, the handleStuff function always uses the initial value of 100 (so its always 100 + 10) instead of incrementing by 10 each time a key is pressed. I can solve this by removing the empty [] as the second argument in useEffect. However, I expected the handleStuff to still update with the latest value instead, given that the eventListner has already been added on initial render and on second keydown it should add 10 to 110 instead of 100, but it keeps using the initial state value of 100 instead. Why must you clear the old listener and add the new one each time for it work?
[value, setValue] = useState(100)
handleStuff = (event) => {
setValue(value+10)
}
useEffect(() => {
window.addEventListener('keydown', handleStuff)
return () => {
window.removeEventListener('keydown', handleStuff)
}
},[]);
I am not really insterested in the solutions, I am really insterested in understanding how useEffect() and useState() hooks function in this conditions.
The problem seems to be that event listener however always uses that very first initial state with which it was rendered
this is called a closure – ThomasuseState
hook would be useless. It would produce this:const value = initialValue;
AndsetValue
would be pointless because by the next call,value
would be reset to the initial value. – Thomas