RomeNYRR
RomeNYRR

Reputation: 887

React Incremeting Counter Behind

I've just learned to use UseState to create a simple incremental counter, but I've noticed some odd behavior with the count being 2 numbers behind (+ - ) in console.log. Now on the screen the number displays fine, but this creates an issue because I'm trying to change the color of the number if it's negative or positive.

Because I'm trying to change the display color of the number on the screen, would UseEffect be a good solution to this problem? I'm going to go back and watch some YT videos on UseEffect, but figured I'd ask here as well. I was thrilled when I was able to figure out how to change the classnames using state, but then got a pie in the face when the numbers weren't changing colors correctly.

Here's an example of the behavior I'm seeing.

const { useState } = React

function Vote () {
  const [count, setCount] = useState(0)
  const [color, setColor] = useState('black')
  
  function handleDecrement () {
    setCount(count - 1)
    checkCount()
  }
  
 function handleIncrement () {
    setCount(count + 1)
    checkCount();
  }
  
  function checkCount () {
    // Less than 0 make it red
        if (count < 0) {
            setColor('red')
      console.log(count)
    // Greater than 1 make it green
        } else if (count > 0  ) {
            setColor('green')
      console.log(count)
    // If it's 0 just keep it black
        } else {
            setColor('black')
      console.log(count)
        }
    
  };
  
  return (
 <div>
      <button onClick={handleDecrement}>-</button>
      <h1 className={color}>{count}</h1>
      <button onClick={handleIncrement}>+</button>
</div>
  )
}

ReactDOM.render(<Vote />, document.getElementById('root'))

Upvotes: 0

Views: 456

Answers (2)

trixn
trixn

Reputation: 16309

When updating the state based on the current state always use the callback version of setState which receives the current state as an argument and should return the next state. React batches state updates and relying on what has been returned by useState to update can yield incorrect results. Also the way to check for a change to count and update accordingly is by using useEffect with count as a dependency. The console.log() in your example will still log the old state as state updates are async and can only be seen during the next render.

const [count, setCount] = useState(0)
const [color, setColor] = useState('black')

function handleDecrement () {
    setCount(current => current - 1);
}

function handleIncrement () {
    setCount(current => current + 1)
}

useEffect(() => {
    // Less than 0 make it red
    if (count < 0) {
        setColor('red')
        console.log(count)

    // Greater than 1 make it green
    } else if (count > 0  ) {
        setColor('green')
        console.log(count)

    // If it's 0 just keep it black
    } else {
        setColor('black')
        console.log(count)
    }

}, [count]);

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 202686

Yes, you can simply use an effect hook with dependency to check the color. When count updates the effect hook callback is triggered.

The issue is that react state updates are asynchronous, so the updated state count won't be available until the next render cycle; you are simply using the count value from the current render cycle.

Note: When incrementing/decrementing counts you should use a functional state update. This ensures state is correctly updated from the previous state in the case multiple state updates are enqueued within any single render cycle.

function Vote() {
  const [count, setCount] = useState(0);
  const [color, setColor] = useState("black");

  function handleDecrement() {
    setCount(count => count - 1);
  }

  function handleIncrement() {
    setCount(count => count + 1);
  }

  useEffect(checkCount, [count]);

  function checkCount() {
    // Less than 0 make it red
    if (count < 0) {
      setColor("red");
      console.log(count);
      // Greater than 1 make it green
    } else if (count > 0) {
      setColor("green");
      console.log(count);
      // If it's 0 just keep it black
    } else {
      setColor("black");
      console.log(count);
    }
  }

  return (
    <div>
      <button onClick={handleDecrement}>-</button>
      <h1 className={color}>{count}</h1>
      <button onClick={handleIncrement}>+</button>
    </div>
  );
}

Edit react-incremeting-counter-behind

Upvotes: 1

Related Questions