JSL
JSL

Reputation: 59

Comprhension about useState (React)

Can you please explain me why this simple code doesn't work as expected ? There's a button called "Start the timer" that's directing to the "chrono" function. I thought that using setCount would update the count variable state but it stays at 0 every second. Isn't useState used precisely to update variables ? Thanks for your help ! PS : in the button, I just rename onClick to clicAction to use it in my component but this is not a problem at all.

import { Button } from './components/Button';
import { useState, useEffect } from 'react';

function App() {

  const [count, setcount] = useState(0);
  
 const chrono = () => {
   setInterval(() => {
    setcount(count +1);
    console.log(count);
   }, 
   1000);
 }

  return (
    <>
    <Button clicAction={chrono} nom="Start the timer"/>
    <p>{count}</p>
    </>
  );
}

export default App;

And here's the Button component code :

import { useState } from 'react';

export const Button = ({nom, clicAction}) => {

    return (
        <div>
            <button onClick={clicAction}>{nom}</button>
        </div>
    )
}

Upvotes: 3

Views: 77

Answers (2)

yaputra jordi
yaputra jordi

Reputation: 533

The reason it does not work is because you are using the current count value to determine the next count value. It might not work as expected because as mentioned in the React official documentation, React tends to call setState asynchronously for performance reason. Therefore for state, you should not rely on their values for calculating the next state. ~React. Instead, you might want to pass a function to the setCount argument like this:

const chrono = () => {
  setInterval(() => {
    setcount(prevCount => prevCount + 1);
  }, 
  1000);
}

Edit

As mentioned by @Martin, this behavior is caused not by asynchronicity but closure. So my explanation that mentioned the asynchronous behavior was not correctly used in this context. For the problem above, using function as setState argument is still correct in case of closures. Please refer to @DrewReese's answer if you are looking for closure explanation.

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 203522

This is a stale enclosure of the count state in the interval callback. The value of count is never a newer reference. Use a functional state update to correctly update from the previous count state value.

If you want to log state updates then use an useEffect to log the count state when it updates.

const [count, setCount] = useState(0);

useEffect(() => {
  console.log(count);
}, [count]);


const chrono = () => {
  setInterval(() => {
    setCount(count => count +1);
  }, 1000);
}

Upvotes: 2

Related Questions