Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react set state callback correct way to pass an argument

I am using React's setState method, and calling another function when the state has been updated.

Is there a preferred approach as to how to call the function that is passed to setState as a callback.

Both of the approaches below work but is there any performance implications of using one over the other?

  this.setState(prevState => {
    return {
      result: '1-0'
    }
  }, this.clearResult(500))

or

  this.setState(prevState => {
    return {
      result: '1-1',

    }
  }, () => this.clearResult(500))

My clearPin method looks like the following. All of this code is within a React component.

  clearResult(time) {

    setTimeout(() => {
      this.setState({
        result: '0-0'
      })
    }, time)

  }
like image 209
peter flanagan Avatar asked Dec 07 '22 15:12

peter flanagan


1 Answers

Both of the approaches below work but is there any performance implications of using one over the other?

There's a correctness implication: The first one is incorrect, the second one is correct. :-)

In your first example, you're calling this.clearResult(500) and then calling setState (with the result of calling this.clearResult(500) — undefined, in your example — as its second argument). this.setState(prevState => { ... }, this.clearResult(500)); is just like foo(bar()) — first it calls bar, then it passes the result of calling it into foo.

In your second example, you're passing a function into setState that it will call when the state is updated.

You want the second form (or one of the various equivalents to it).

this.setState(prevState => {
  return {
    result: '1-1',
  }
}, () => this.clearResult(500));
// or:  }, this.clearResult.bind(this, 500));
// But the arrow is clear and idiomatic

Here's proof that your first example is calling clearResult before calling setState, and before your state change callback is called:

class Example extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {value: "a"};
  }
  
  // Overriding it PURELY to show what's happening
  setState(...args) {
    console.log("setState called");
    return super.setState(...args);
  }
  
  componentDidMount() {
    this.setState(
      () => {
        console.log("state change callback");
        return {value: "b"};
      },
      this.clearResult(500)
    );
  }
  
  clearResult(delay) {
    console.log("clearResult called");
    setTimeout(() => {
      this.setState({value: "c"});
    }, delay);
  }
  
  render() {
    return <div>{this.state.value}</div>;
  }
}

ReactDOM.render(
  <Example />,
  document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

whereas with () => this.clearResult(500) instead, clearResult is called after setState (and after the state change):

class Example extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {value: "a"};
  }
  
  // Overriding it PURELY to show what's happening
  setState(...args) {
    console.log("setState called");
    return super.setState(...args);
  }
  
  componentDidMount() {
    this.setState(
      () => {
        console.log("state change callback");
        return {value: "b"};
      },
      () => this.clearResult(500)
    );
  }
  
  clearResult(delay) {
    console.log("clearResult called");
    setTimeout(() => {
      this.setState({value: "c"});
    }, delay);
  }
  
  render() {
    return <div>{this.state.value}</div>;
  }
}

ReactDOM.render(
  <Example />,
  document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Side note 1: If you want, you can be a bit more concise:

this.setState(
    () => ({ result: '1-1' }),
    () => this.clearResult(500)
);

Side note 2: There's no need to use the function form if the new state you're passing isn't based on current state or props. In your example, it isn't, so yours is one of the places where using the non-callback form is okay:

this.setState(
    { result: '1-1' },
    () => this.clearResult(500)
);

That would not be okay if you were using something from this.state or this.props. In that situation, use the callback form and its prevState and props parameters. Always. And there's little harm in always using the callback form, the overhead of a function call is exceptionally trivial on modern JavaScript engines. (It was even trivial on the slowest JS engine of this century: The one in IE6.)

More on that here and here.

like image 115
T.J. Crowder Avatar answered Dec 10 '22 04:12

T.J. Crowder