Functional setState() causes Null Error

Here I have error “Cannot read property ‘value’ of null”.


class DisplayMessages extends React.Component {
constructor(props) {
  super(props);
  this.state = {
    input: '',
    messages: []
  }
}
// add handleChange() and submitMessage() methods here
handleChange(event) {
  this.setState(state => {
    return {
      input: event.target.value,
      messages: state.messages
    }
  })
}

render() {
  return (
    <div>
      <h2>Type in a new Message:</h2>
      { /* render an input, button, and ul here */ }
      <input onChange={this.handleChange.bind(this)} value={this.state.input}/>
      { /* change code above this line */ }
    </div>
  );
}
};

It functions well when I use setState like this, the same as the challenge’s guide and without changing input property, with this keyword attaches to messages state:

this.setState({
      input: event.target.value,
      messages: this.state.messages
    })

Is this error caused by using this keyword? If yes, why? As far as I know, it is recommended to prevent using this keyword inside setState(). I thought it was okay to use this this way because the app being coded in the challenges were simple and didnt involve asynchronous functions. If no, then is it okay to continue using this inside setState?

Link to the challenge:
https://www.freecodecamp.org/learn/front-end-libraries/react-and-redux/manage-state-locally-first

Um, setState is asynchronous, this is why you use the function callback version if you have to compute from current state

Ah yes thanks for reminding me setState is also an async function. However as I read from React Doc, both passing an object and passing a callback as the first param are good to go. I just dont understand why my code using callback went wrong while the one using object was totally fine :frowning:

I have found the explaination of this and successfully fixed it.
TypeError: “Cannot read property ‘value’ of null” is caused in this scenerio due to the event param passed into handleChange() func, instead of this keyword’s fault as I thought.
Quoted from React Doc:

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.

As above, event is considered SyntheticEvent in React and can not be accessed in an asynchronous way, which is setState() here. Event is reused and nullified after being used, that’s why I had the error.
A workaround for this problem is to use event.persist() as the doc goes:

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.

Besides, conventional setState() (pass an object as the first argument) works because it directly does the work without fetching previous version of state as the functional one, so that it won’t take time for the event to be nullified.

Thanks for the help guys!
For now I will mark this as the solution. Please correct me if I’m wrong or leave comments if you have any additional ideas.

1 Like