pumpum
pumpum

Reputation: 645

Issue of using e.target.value in React setState function

I am experiencing a strange issue in React. I think probably I have not fully grasped how React works and I really appreciate your help.

Please find below my React component:

class myComponent extends Component {
  state = {
    value: ''
  }

  updateValue = e => {
    this.setState({
      value: e.target.value
    })
  }

  render() {
    return (
     <form>
      <input type="text" onChange={this.updateValue} defaultValue={this.state.value} />
     </form>
    )
  }
}

Then, now if I type something in the text field, I will get following warning and errors:

Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property target on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().

Uncaught TypeError: Cannot read property 'value' of null

But if I change the 'updateValue' method to:

updateValue = e => {
    const newValue = e.target.value
    this.setState({
      value: newValue
    })
  }

It will work without any issue.

Upvotes: 14

Views: 15852

Answers (4)

zemmsoares
zemmsoares

Reputation: 353

On functional components

const [name, setName] = useState('');

<input onChange={(e) => setName(e.target.value)} />

Upvotes: 0

ray
ray

Reputation: 171

If someone is looking for the way to make work this. Here a snipped.

class myComponent extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
    this.updateValue = this.updateValue.bind(this);
  }
  updateValue(e) {
    this.setState({ value: e.target.value })
  }
  render() {
    return ( <form>
      <input type="text" value={this.state.value} 
        onChange={this.updateValue} 
        defaultValue={this.state.value} />
      <h4>Controlled Input:</h4> <p>{this.state.value}</p>
      </form>
    ); 
  }
};

Upvotes: 0

tskjetne
tskjetne

Reputation: 2354

Updated answer

As @FelixKling pointed out, my answer wasn't entirely correct. My answer is only valid if you pass a function as a parameter to setState, not if you pass an object like done in the question.

If you pass an object to setState, the parameter will be evaluated immediately, that is, before the event has been nullified. As such, the mentioned error won't occur.

If you pass a function to setState, the parameter will be evaluated within that function, which happens after updateValue has completed/returned and the event has been nullified. If you pass a function to setState, you must store the e.target.value in a variable (or call e.persist()) before setState.

Old (slightly wrong) answer

This problem occurs because (as the error message says) the event is set to null when the callback (your updateValue function) is completed.

Because this.setState() is an asynchronous function, it will not be executed immediately. As a result, this.setState() is actually executed after your updateValue is finished, and hence after the event has been nullified.

Your solution is actually the proposed way from the React docs!

Upvotes: 12

joknawe
joknawe

Reputation: 1540

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.

Basically, the click event (e) is not accessible inside the asynchronous setState() due to React's Event Pooling, unless you use event.persist() (as mentioned in the warning you received).

React Event Pooling: https://reactjs.org/docs/events.html

Upvotes: 4

Related Questions