question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

onChange doesn't fire if input re-renders due to a setState() in a non-React capture phase listener

See original GitHub issue

Extracting from https://github.com/facebook/react/issues/12643.

This issue has always been in React. I can reproduce it up to React 0.11. However it’s probably extremely rare in practice and isn’t worth fixing. I’m just filing this for posterity.

Here is a minimal example.

class App extends React.Component {
  state = {value: ''}
  handleChange = (e) => {
    this.setState({
      value: e.target.value
    });
  }
  componentDidMount() {
    document.addEventListener(
      "input",
      () => {
        // COMMENT OUT THIS LINE TO FIX:
        this.setState({});
      },
      true
    );
  }
  render() {
    return (
      <div>
        <input
          value={this.state.value}
          onChange={this.handleChange}
        />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));

Typing doesn’t work — unless I comment out that setState call in the capture phase listener.

Say the input is empty and we’re typing a.

What happens here is that setState({}) in the capture phase non-React listener runs first. When re-rendering due to that first empty setState({}), input props still contain the old value ("") while the DOM node’s value is new ("a"). They’re not equal, so we’ll set the DOM node value to "" (according to the props) and remember "" as the current value.

screen shot 2018-08-17 at 1 08 42 am

Then, ChangeEventPlugin tries to decide whether to emit a change event. It asks the tracker whether the value has changed. The tracker compares the presumably “new” node.value (it’s "" — we’ve just set it earlier!) with the lastValue it has stored (also "" — and also just updated). No changes!

screen shot 2018-08-17 at 1 10 59 am

Our "a" update is lost. We never get the change event, and never actually get a chance to set the correct state.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:9
  • Comments:13 (3 by maintainers)

github_iconTop GitHub Comments

12reactions
vinay72commented, Sep 25, 2018

Instead of using this onChange={this.handleChange}, you can use onChange={event => this.handleChange(event.target.value)} />

10reactions
owenDodscommented, Apr 23, 2020

Oh man, I wish I’d stumbled across this thread earlier - was banging my head against this issue for a while and thought I was going insane.

I managed to reproduce my simplified use case here.

I have an application where I’ve started to add in keyboard shortcuts, hence why I was making use of document.addEventListener as well as other form inputs on the page.

What I was seeing in my app while I was trying to debug this really confused me. When I added debugger statements within the relevant useEffect hook I could see the text appear in the input then disappear within the useEffect hook’s cleanup function - all without firing the input’s onChange.

While I appreciate that it might not be pragmatic to try and fix what might be considered an “extremely rare” issue; how realistic would it be to add a warning for users about this potential conflict? Maybe even just adding a “N.B.” to the documentation?

When I searched for “addEventListener” on the reactjs.org site it took me to Handling Events. Maybe a note here could save people some hassle? Or making a “Gotchas” page for known edge-case issues like this that are known but aren’t going to be fixed anytime soon?

In any case, thanks for filing this issue in the first place - it gave me some much needed closure!

Read more comments on GitHub >

github_iconTop Results From Across the Web

onChange in React doesn't capture the last character of text
When are you logging the state? Remember that setState is asynchronous, so if you want to print the new state, you have to...
Read more >
React Interview Questions and Answers (2023) - InterviewBit
Any change in the property values of the state object leads to the re-rendering of the component. Note- State object is not available...
Read more >
Fix the slow render before you fix the re-render - Kent C. Dodds
A "commit" is when React takes those differences and makes the DOM updates. render → reconciliation → commit ↖ ↙ state change. To...
Read more >
useState vs. useRef: Similarities, differences, and use cases
Learn the similarities and differences between the useState and useRef Hooks in React, as illustrated demos and use cases.
Read more >
Event Bubbling and Event Catching in JavaScript and React
✨ Updated Event Firing Order and useCapture Param in JavaScript; ✨ Which Events Do Not Bubble and How Are They Handled? ✨ Event...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found