Tao Gómez Gil
Tao Gómez Gil

Reputation: 2705

Several methods for setState instead of single one

I'm developing a React app without Redux or any other state manager.

Let's say I want to do three things when a button is clicked:

  1. Enable some other button
  2. Remove a label
  3. Show a confirmation toaster

These 3 things are controlled by 3 variables of the state. I could therefore do simply this:

myHandler = () => {
    this.setState({
        canSave: true,                      // Enable the button
        isLabelVisible: false,              // Hide label
        isConfirmationMessageVisible: true, // Show confirmation message
    });
}

However, I could get rid of those comments by using some private class functions, like this:

myHandler = () => {
    this.toggleSaveButton(true);
    this.toggleLabel(false);
    this.toggleConfirmationMessage(true);
}

toggleSaveButton= (enabled) => {
    this.setState({
        canSave: enabled,
    });
}

toggleLabel= (visible) => {
    this.setState({
        isLabelVisible: visible,
    });
}

toggleConfirmationMessage= (visible) => {
    this.setState({
        isConfirmationMessageVisible: visible,
    });
}

In addition to remove those comments which could easily get out-of-sync with the code, this allows me to reuse the private methods in other places of my code.

Since this is handled in a synthetic event, I have read here that it will be batched, so I can expect no performance penalty.

My question is: is this good practice? have you used this approach? can you point some potential drawbacks I can not foresee right now?

Upvotes: 3

Views: 68

Answers (1)

Chris
Chris

Reputation: 59511

This is perfectly fine. As you mention, React batches all updates to the state that are triggered from event handlers. This means that you can safely use multiple setState() like you are doing here.

In current release, they will be batched together if you are inside a React event handler. React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.

The only thing you need to look out for is if you are changing the same state twice from two setState() calls. For example:

a() {
  this.setState({foo: foo+1});
}
b() {
  this.setState({foo: foo+1});
}

Calling a() and then b() from the same event, will increment foo by 1, not two.

Instead use:

a() {
  this.setState(prevState => ({foo: foo+1}));
}
b() {
  this.setState(prevState => ({foo: foo+1}));
}

this will correctly increment foo by 2.


For potential future readers who are not calling multiple setState() from an event handler, I should note the following:

With current version, several setStates outside of event handlers (e.g. in network responses) will not be batched. So you would get two re-renders in that case.


Alternative solution

What you can do though, regardless if you call setState() from an event handler or not, is to build an object and then set it as the new state. The potential benefit here is that you only set the state once and thus don't rely on batching or where the function was triggered from (event handler or not).

So something like:

myHandler = () => {
    let obj = {}
    obj = this.toggleSaveButton(obj, true);
    obj = this.toggleLabel(obj, false);
    obj = this.toggleConfirmationMessage(obj, true);
    this.setState(obj);
}

toggleSaveButton= (obj, enabled) => {
    obj.canSave = enabled;
    return obj;
}

toggleLabel= (visible) => {
    obj.isLabelVisible = visible;
    return obj;
}

toggleConfirmationMessage= (visible) => {
    obj.isConfirmationMessageVisible = visible;
    return obj;
}

Upvotes: 1

Related Questions