Reputation: 2290
I'm using a functional component. I'm aware setColor
changes the value of color
asynchronously. However, my callback function doesn't receive the updated version of color
(blue) even when it executes well after color
has been updated. Here's an abstract version of my code:
let [color, setColor] = useState("red");
useEffect(() => {
setColor("blue");
setTimeout(() => {
console.log(color);
}, 5000)
}, []);
(here's a sandbox: https://codesandbox.io/s/falling-cherry-17ip2?file=/src/App.js)
My only guess is that the setColor
function almost creates a new color
variable & console.log
is stuck referencing the old color
.
Troubleshooting
I'm aware a secondary useEffect
has the potential to execute my callback when state changes. However, this is inconvenient because I'm right in the middle of complex logic where I only want the callback to execute under certain conditions.
I'm also aware useRef
variables update pretty much immediately and so that'd be an alternative.
Nevertheless, the question still stands: why isn't the updated value of color
being logged & is there anything I could do in the primary useEffect
to access the latest version of color
state?
Upvotes: 1
Views: 84
Reputation: 12919
Here is a snippet logging the color
state value at various times and also showing the effect of cleaning up in the return of the useEffect. It only serves to illustrate the timeline that Quentin laid out in their answer.
const App = ()=>{
const [color, setColor] = React.useState("red");
console.log('outside: ', color);
React.useEffect(() => {
setColor("blue");
console.log('inside: ', color);
setTimeout(() => {
console.log('inside-timed: ', color);
}, 5000)
// the cleaned-up timer won't fire
const timer = setTimeout(() => {
console.log('timer: ', color);
}, 5000)
return (clearTimeout(timer));
},[])
return (
<div style={{backgroundColor: color, width: '40px', height: '40px'}} ></div>
)
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Upvotes: 1
Reputation: 943537
setColor
changes the state.let [color, setColor] = useState("red");
color
to the color
variable.setTimeout
runs. This has closed over the previous color
variable which was assigned the old state.Upvotes: 2