0

I know there are other articles and posts on this topic and almost all of them say to use the ! operator for a Boolean state value. I have used this method before but for the life of me I can not toggle this Boolean value.

import { useState } from 'react';

const [playerTurn, setPlayerTurn] = useState(true);

const changePlayerTurn = () => {
        console.log(playerTurn); // returns true
        setPlayerTurn(!playerTurn);
        console.log(playerTurn); // also returns true
};

changePlayerTurn();

I have also tried setPlayerTurn(current => !current), commenting out the rest of my code to avoid interference, and restarted my computer in case that would help but I am still stuck with this issue.

Can anyone point out why this is not working?

Bryan F
  • 49
  • 4
  • Does this answer your question? [React setState not Updating Immediately](https://stackoverflow.com/questions/38558200/react-setstate-not-updating-immediately) – isherwood Feb 08 '23 at 15:46

4 Answers4

0

This is happening because setPlayerTurn is async function. You can use another hook useEffect() that runs anytime some dependencies update, in this case your playerTurn state.

export default YourComponent = () => {
  const [playerTurn, setPlayerTurn] = useState(true);

  useEffect(() => {
    console.log('playerTurn: ', playerTurn);
  }, [playerTurn]);

  const changePlayerTurn = () => {
    setPlayerTurn(!playerTurn);
  }

  return (
    <button onClick={changePlayerTurn}>Click to change player turn</button>
  );
}

Basically whenever you use setState React keeps a record that it needs to update the state. And it will do some time in the future (usually it takes milliseconds). If you console.log() right after updating your state, your state has yet to be updated by React. So you need to "listen" to changes on your state using useEffect().

useEffect() will run when your component is first mounted, and any time the state in the dependencies array is updated.

derFrosty
  • 550
  • 3
  • 17
0

The setPlayerTurn method queues your state change (async) so reading the state directly after will provide inconsistent results.

If you use your code correctly in a react component you will see that playerTurn has changed on the next render

0

You creating a async function, to solve this you can create a button in your component, which will run the function and you can use the "useEffect" hook to log every time the boolean changes... so you can see the changes taking place over time, like this:

import React, { useEffect } from "react";
import { useState } from "react";

const Player = () => {
  const [playerTurn, setPlayerTurn] = useState(true);

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

  return <button onClick={() => setPlayerTurn(!playerTurn)}>change player turn</button>;
};

export default Player;
0

The value of the state only changes after the render. You can test this like:

// Get a hook function

const Example = ({title}) => {
    const [playerTurn, setPlayerTurn] = React.useState(true);
    
    React.useEffect(() => {
      console.log("PlayerTurn changed to", playerTurn);
    }, [playerTurn]);
    
    console.log("Rendering...")
  
    return (<div>
      <p>Player turn: {playerTurn.toString()}</p>
      <button onClick={() => setPlayerTurn(!playerTurn)}>Toggle PlayerTurn</button>
    </div>);
};


// Render it
ReactDOM.render(
    <Example />,
    document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

The callback inside the useEffect runs during the component mount and when one of the values inside the second argument, the dependecy array, changes. The depency here is playerTurn. When it changes the console will log. As you will see, before this happens, the "Rendering..." log will appear.

DerAnonyme
  • 580
  • 8