tiggybits
tiggybits

Reputation: 119

Calling setState in an eventHandler in the render method

When using React I've understood it so that calling setState inside a component's render method is bad practice; instead keep this method pure. However, if I need to update the state based on an event that is linked to a component, how do I do this?

class Toggle extends React.Component {
   constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

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

  render() {
    return (
       <button onClick={this.handleClick}>
         {this.state.isToggleOn ? 'ON' : 'OFF'}
       </button>
    );
  }
}

The code above is from React's official tutorial. As we can see, an eventhandler is bound to the onClick attribute, and inside this eventhandler the state of the component is changed, so potentially we will update the state while calling from a render method. Thus I am a bit lost as to why this is alright to do but not calling setState explicitly in the render method? Has it to do with how React wraps plain html-events?

Upvotes: 2

Views: 7791

Answers (3)

Ali Elzoheiry
Ali Elzoheiry

Reputation: 1682

What is bad practice is to call setState explicitly in the render method, because the render method should be able to run multiple times without having any side effects (Without affecting anything outside the render itself)

In the code block above, the setState is bound to a click handler, so that means it is only invoked when the button is clicked, not when the render method is called, so this is completely fine.

To recap:

It is fine to have event handlers that update the state in the render method, as long as they are only called when that event is fired.

It is not good to have setState or a function that calls setState directly in the render function.

EXAMPLE

doSomthing = () => {
  this.setState({ foo: 'bar' });
}

render() {
  return (
    <button onClick={this.doSomething}>Click Me</button>
  );
}
// this code is GOOD

The above example is OK

doSomething = () => {
  this.setState({ foo: 'bar' });
}

render() {
  this.doSomething();
  return (
    <button>Click Me</button>
  );
}
// this code is BAD

The above example is BAD

Upvotes: 3

render() {
    return (
       <button onClick={this.handleClick}>
         {this.state.isToggleOn ? 'ON' : 'OFF'}
       </button>
       {this.setState(() => ({}))}
    );
  }

When you are trying to do something like above. It causes rendering issue. Think what is the purpose of the setState(). Obviously to change the state of component. Now think what happens when your state changes. Yes your component is rendered again. Now think if a component is rendering and it finds the setState() again then it will cause the rendering to malfunction.

Now over to your issue of event handler a good practice of using setState()

It is simple concept, we are using reference in event call that means when the component is rendered it is not going to fire it immediately rather it will wait for someone to call. It is clear that when someone is going to invoke the call then only state is changed and it will cause no issue to render() and it will work properly

Upvotes: 1

dvvtms
dvvtms

Reputation: 627

there is function not called, only passed :

<button onClick={this.handleClick}>
         {this.state.isToggleOn ? 'ON' : 'OFF'}
</button>

Upvotes: 1

Related Questions