2

What's the major difference bewteen these two functions?

handleOnChange(evt) {
    this.setState(() => ({
        tickerName: evt.target.value
    }));
}

handleOnChange(evt) {
    this.setState({ tickerName: evt.target.value });
}

And why with the handleOnChange() function that change the state directly this works fine?

<input
    type="text"
    value={this.state.tickerName}
    onChange={(evt) => this.handleOnChange(evt)} 
/>

When I use the first function that change the state with a callback I get this error:

TypeError: evt.target is null
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
Paolo Guerra
  • 259
  • 1
  • 9
  • https://stackoverflow.com/a/42090588/4467208 – Murat Karagöz Jan 03 '18 at 10:23
  • 1
    for more details about setState check the [**DOC**](https://reactjs.org/docs/react-component.html#setstate), updater function is required when next state value is dependent on previous state value, and in your case it's not. – Mayank Shukla Jan 03 '18 at 10:31

2 Answers2

8

These are two different syntaxes for setState

First:

handleOnChange(evt) {
    this.setState(() => ({
        tickerName: evt.target.value
    }));
}

uses the updater function as the first argument.

Second:

handleOnChange(evt) {
   this.setState({ tickerName: evt.target.value });
}

uses the object to be updated

When using the synthetic event in the updater function you need to use event.persist()

From the documentation:

the SyntheticEvent is pooled. This means that the SyntheticEvent object will be reused and all properties will be nullified after the event callback has been invoked. This is for performance reasons. As such, you cannot access the event in an asynchronous way.

If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.

Your fist case would look like

handleOnChange(evt) {
    evt.persist();
    this.setState(() => ({
        tickerName: evt.target.value
    }));
}

Or instead of using event.persist() you could store the event in another object

handleOnChange(evt) {
    const value = evt.target.value;
    this.setState(() => ({
        tickerName: evt.target.value
    }));
}

P.S. You should use the updater function for setState only when you wish to update the current state based on prevState or props

CodeSandbox

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • So in my case is it quite useless to update the state with a callback? Because I read that the new standard way for update the state is with a callback, isn't it?. – Paolo Guerra Jan 03 '18 at 10:53
  • 1
    The updater function in `setState` has format `(prevState, props) => stateChange`, so if you don't need access to the previous state I would say go with the second one that just takes an update object. [React docs](https://reactjs.org/docs/react-component.html#setstate) has very good explanation on methods of calling `setState`. – margaretkru Jan 03 '18 at 10:57
  • @margaretkru, yes thats the most basic thing we should care about. – Shubham Khatri Jan 03 '18 at 12:10
  • @Shubham Khatri, do you know if there is any detailed and documented explanation to why there is no need to call `event.persit()` or cache values from the event when calling `setState` with an object, but there is a need to do so when using updater function? Shouldn't `setState` work async in both cases? Even react docs on [controlled components](https://reactjs.org/docs/forms.html#controlled-components) don't use of `event.persist()` . – margaretkru Jan 03 '18 at 12:18
  • 3
    @margaretkru, so when you simply write `this.setState({ tickerName: evt.target.value });` , you are simply passing an object to setState, and the . value to tickerName key is assigned immedialtely, however in the updater function its nothing but a callback invoked with the necessary parameters by setState and by that time the event properties are nullified. Hope I could make it clear – Shubham Khatri Jan 03 '18 at 12:23
  • wow, how could I have not realized that ... brain fart) @Shubham Khatri, thanks a lot for the explanation! – margaretkru Jan 03 '18 at 12:30
2

React wraps events in its own SyntheticEvent which will be reused and all properties will be nullified after the event callback has been invoked (see react docs).

According to react docs, one can't access react SyntheticEvent in an asynchronous way and setState is async. So when setState is executed your evt might be null because it gets reused by react. Since the first function uses a callback with access to previous state in setState, by the time the callback is executed the event evt is reused and nullified by react. In the second case the update object with the event value is created right away and passed in directly and it doesn't create a similar problem.

If you need to use setState with a callback you need to take care of the event possibly being null when the callback is executed. One way of dealing with this would be store the original reference to target outside of setState it will get scoped and used inside setState. Like this:

handleOnChange(evt) {
    const target = evt.target;
    this.setState((prevState) => ({
        tickerName: target.value
    }));
}

I wrote a more detailed answer to why react events get null in setState here.

margaretkru
  • 2,751
  • 18
  • 20