0

This Codepen toggles a button value from true to false.

I understand this apart from how this handleClick function is working:

 handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

I am interested in how the function retrieves the isToggleOn bool value.

I know we can't directly use !this.state.isToggleOn in setState - why is the handleClick function more reliable in this scenario for a React beginner?

This is where the handleClick function is called:

render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
halfer
  • 19,824
  • 17
  • 99
  • 186
nipy
  • 5,138
  • 5
  • 31
  • 72
  • If you need to change your state based on the previous state you could use, ```this.setState(prevState...``` .. ```!prevState.isToggleOn``` will switch the values between true and false on each click .. – Maniraj Murugan Apr 21 '20 at 14:28
  • Does this answer your question? [When to use functional setState](https://stackoverflow.com/questions/48209452/when-to-use-functional-setstate) – Emile Bergeron Apr 21 '20 at 14:47

3 Answers3

2

From the setState docs:

The first argument is an updater function with the signature:

(state, props) => stateChange

state is a reference to the component state at the time the change is being applied.

In your example prevState is state renamed, and it contains all of the assignments in state, including isToggleOn.

You could alternately just read this.state in the setState function. The prevState pattern is used to communicate that this is the old/current state, the state that is being changed.

setState docs

Jake Worth
  • 5,490
  • 1
  • 25
  • 35
0

In this scenario you can actually use both, because it's really simple and you call the setState from the render function. In class components is a bit hard to find a place where this this.setState({oldState:this.state.oldState}) can be problematic. On the contrary, if you use functional components or hooks, you can see this problem quite often.

JS is not Object oriented!

To understand this you have to think first that js is not an object oriented language. React tries just to simulate it. Basically when you change a state, the class containing it gets called again trying to run only the code which changes. In your case, when handleClick is called, the render function get triggered because the state has changed!

Said this, handleClick is not just a function! It's also a property of the class. React tries to understand whether it should "re-declare" those functions depending on the values they use.

Good Practice

In the case you wrote, isToggleOn: !prevState.isToggleOn, the function is indipendent. It doesn't need any other property of the class (apart of setState, which works with a different pattern), so after the state changes, the react engine doesn't have to set the function again.

Bad Practice (sometimes much much easyer to write)

On the contrary if you use isToggleOn: !this.state.isToggleOn the handleClick function is dependent of this.state so every time it changes, the react engine has to "re-declare" the function by sort of substituting this.state with {isToggleOn:...}

How to recreate that pattern

function myRandom(property){
  //Kind of private Variable
  let hiddenVar=Math.random();
  return property(hiddenVar);
}

//Let's say here the Math class is unreachable while myRandom is.
myRandom((num)=>{
  console.log(num);//This ac
})
Community
  • 1
  • 1
Andrea
  • 1,286
  • 6
  • 18
-3

Maybe just check the docs on this?

The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.

In essence, you are getting the previous instance of the state in the function, then making a change to the state and returning it. This new state will get saved and cause a re-render of your component.

If you use functional components, you can negate having to pass a callback here and could do something like:

function MyComponent() {
  const [on, setOn] = React.useState(false)

  return (
    <button onClick={() => setOn(!on)}>
      {on? 'ON' : 'OFF'}
    </button>
  );
}
Dana Woodman
  • 4,148
  • 1
  • 38
  • 35
  • I thought we should not use this.state as per React docs [Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.](https://reactjs.org/docs/state-and-lifecycle.html) – nipy Apr 21 '20 at 14:55
  • I was not aware of this because I don't use class components anymore and assumed React would handle this. I'll update the answer to reflect that – Dana Woodman Apr 21 '20 at 16:53