ESLint: Use callback in setState when referencing the previous state

13,426

Solution 1

You can remove ...this.state from setState because it will only update parameters in the state you have changed.

You can see in the React documentation that setState(stateChange) performs a performs a shallow merge of stateChange into the state.

Object.assign(
  previousState,
  stateChange,
  ...
)

This means you don't need to be passing your previous state in, unless new properties depend on it, as setState will do this merge for you.

handleSelect(event) {
    const entry = event.nativeEvent;

    if (entry == null) {
      this.setState({ selectedEntry: entry });
    } else {
      this.setState({
        selectedEntry: JSON.stringify(entry),
        markerIsEnabled: true
      });
    }

    console.log(event.nativeEvent);
  }

Solution 2

As you can see in documentation

This rule should prevent usage of this.state inside setState calls. Such usage of this.state might result in errors when two state calls are called in batch and thus referencing old state and not the current state. An example can be an increment function:

function increment() {
  this.setState({value: this.state.value + 1});
}

If these two setState operations is grouped together in a batch it will look be something like the following, given that value is 1:

setState({value: 1 + 1})
setState({value: 1 + 1})

This can be avoided with using callbacks which takes the previous state as first argument:

function increment() {
  this.setState(prevState => ({value: prevState.value + 1}));
}

So this is why you have that rule, to avoid erros like this example.

In your case, what you should do is

handleSelect({ nativeEvent }) {    
    if (nativeEvent == null) {
        this.setState(previousState => ({
            ...previousState, 
            selectedEntry: nativeEvent
        }));
    } else {
      this.setState(previousState => ({
        ...previousState,
        selectedEntry: JSON.stringify(entry),
        markerIsEnabled: true
      }));
    }
}

But for your case, this an error won't happen because you don't have two consecutives setState and also, ...this.state or ...prevState won't make any diference because you aren't using the previous state to set a new state.

So for the code you provided in your question, just remove ...this.state and it will work fine, with no errors.

Solution 3

Try this. It's because setState is async.

handleSelect({ nativeEvent }) {

    if (nativeEvent == null) {
        this.setState((previousState) => ({
            ...previousState, selectedEntry: nativeEvent
        }));
    } else
      this.setState((previousState) => ({
        ...previousState,
        selectedEntry: JSON.stringify(entry),
        markerIsEnabled: true
      }));
  }
Share:
13,426
lecham
Author by

lecham

Updated on June 13, 2022

Comments

  • lecham
    lecham almost 2 years

    For this piece of code I am getting eslint(AirBnb config) error: Use callback in setState when referencing the previous state

    Does this error influence performance somehow and how can it be solved?

      handleSelect(event) {
        const entry = event.nativeEvent;
    
        if (entry == null) {
          this.setState({ ...this.state, selectedEntry: entry });
        } else
          this.setState({
            ...this.state,
            selectedEntry: JSON.stringify(entry),
            markerIsEnabled: true
          });
    
        console.log(event.nativeEvent);
      }
    
  • skyboyer
    skyboyer about 5 years
    why is ...previousState is needed here at all?
  • sandrooco
    sandrooco about 5 years
    Right, could be omitted at all - which also removes the need for a callback. 🙄