React: controlled input challenge //bug

Is anyone else seeing this bug: the demonstration in live preview panel is not working. As soon as you type into the input element the entire component vanishes :point_left: :point_left:
My solution however does pass the challenge

class ControlledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: ''
    };
    // change code below this line
this.handleChange = this.handleChange.bind(this);
    // change code above this line
  }
  // change code below this line
handleChange(event){
this.setState(state => ({
  input : event.target.value
}));
}
  // change code above this line
  render() {
    return (
      <div>
        { /* change code below this line */}
<input value={this.state.input} onChange={this.handleChange.bind(this)} ></input>
        { /* change code above this line */}
        <h4>Controlled Input:</h4>
        <p>{this.state.input}</p>
      </div>
    );
  }
};

The error message specifically is Cannot read property ‘value’ of null on 2 separate lines

I’ve was able to make it work by assigning event.target.value to a constant before calling setState and then using this constant in the setState callback. Something like:

handleChange (event) {
  const input = event.target.value
  this.setState(state => ({ input }))
}

I can’t explain why this happens neither why does the tests passes. But I think it has something to do with the fact that setState executes asynchronously when called with a callback.

1 Like

Searched it and found a nice explanation in this stackoverflow topic.

Basically, is what I said. More precisely, there’s this thing called event pooling which basically means that the event object that is passed as an argument to your handleChange method is reused in every other event callback and thus react will nullify it just after your callback has been invoked. So, you can’t use it inside any asynchronous function.

There are three possible solutions in your specific case, the first being the mentioned in the above comment. The others are:

  • Don’t use a callback in your setState call:
  handleChange (event) {
    this.setState({ input: event.target.value })
  }
  • Call event.persist() so the event object is not nullified:
  handleChange (event) {
    event.persist()
    this.setState(state => ({ input: event.target.value }))
  }

Note: event.persist() works by removing the referred event object from the pool but maintaining a reference to it so you can access it later.

Thanks that worked I tweaked it ever so slightly

handleChange(event){
  const input = event.target.value;
this.setState(state => ({
  input : input
}));
}