sbaden
sbaden

Reputation: 565

How do I fix HandleToggleState?

I have a simple material-ui toggle in my react component. I want to use it to toggle state (false/true). If I start with useState(false) the first time I click the toggle it says false rather than true.

I'm wondering if another react hook would solve for this. useEffect, useCallback...

const Component = () => {
  const [toggleValue, setToggleValue] = useState(false);

  const handleToggleChange = () => {
    setToggleValue(!toggleValue);
    console.log("toggle value in handle: " + toggleValue);
  };


  return(
    <FormControlLabel
       control={
          <Switch
             checked={toggleValue}
             onChange={handleToggleChange}
             value="my toggle"
          />
       }
    />
  );
};

I would expect setPreseason(!preseason); to set the state opposite of what it currently is. True to false and false to true.

It probably is but when I log the state on the next line it logs the initial state and will always log the opposite of what the toggle is.

Upvotes: 2

Views: 117

Answers (3)

Asaf Aviv
Asaf Aviv

Reputation: 11770

The state updater function returned by useState is asynchronous

If you need to react to state changes useEffect is the place for it

const Component = () => {
  const [toggleValue, setToggleValue] = useState(false);

  const handleToggleValue = () => {
    setToggleValue(!toggleValue);
  };

  useEffect(() => {
    console.log("toggleValue: " + toggleValue);
      // second argument to useEffect is an array of dependencies
      // this function is going to run every time one of the dependencies
      // changes
  }, [toggleValue])

  return (
    <FormControlLabel
      control={
        <Switch
          checked={toggleValue}
          onChange={handleToggleValue}
          value="my toggle"
        />
      }
    />
  );
}

Upvotes: 1

Federkun
Federkun

Reputation: 36944

The issue is about which value toggleValue is inside the closure. Is not what you expect. Instead pass a callback to setToggleValue. You will get the current state back, that you can then change.

const handleToggleValue = () => {
  setToggleValue((toggleValue) => !toggleValue);
}

Upvotes: 1

Alex B
Alex B

Reputation: 1448

You are doing it correctly, except that toggleValue is just a local variable and is not changed just by calling setToggleValue (the state will be changed, but that happens asynchronously).

You could do something like this instead:

const handleToggleValue = () => {
  const newToggleValue = !toggleValue;
  setToggleValue(newToggleValue);
  console.log('toggleValue: ' + newToggleValue);
}

It depends what your intention is with the new toggle value. This simply fixes what you were doing with the console.log call. But you may run into further trouble after that, given that you are using the new toggle value before the state is updated. But that is for another SO question...

Upvotes: 0

Related Questions