176

I've tried searching around for an answer to this, but most of them are outside the context of React, where onChange triggers upon blur.

In performing various tests, I can't seem to tell how these two events are different (when applied to a textarea). Can anyone shed some light on this?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
ffxsam
  • 26,428
  • 32
  • 94
  • 144
  • 1
    I edited my post to be clearer. I was speaking specifically about textarea, not radio buttons or checkboxes. – ffxsam Jul 07 '16 at 22:48
  • That's quite false, React is not just JS. And events behave slightly differently in some cases (such as `onChange`). And no, pasting text in a textarea (in React) triggers both `onChange` and `onInput`. Feel free to test in a fiddle and you'll see. – ffxsam Jul 07 '16 at 22:54
  • For and – Roko C. Buljan Jul 07 '16 at 23:01
  • I suppose onChange is the safer bet. I even tried to programmatically change the textarea's value, thinking maybe `onChange` would trigger and `onInput` would not, but both trigger. – ffxsam Jul 07 '16 at 23:02
  • Yes, now after reviewing a bit of React Docs, I see that React does quite some things like jQuery, it normalizes events, and yes. onChange seems like a preferred way. And yep. React is nothing more than JS. There's no special magic. Just rules. – Roko C. Buljan Jul 07 '16 at 23:04
  • Well, I won’t get into splitting hairs.. yes, React is obviously JS, but it changes some of the conventions we’re used to with event handling in HTML. – ffxsam Jul 07 '16 at 23:09

6 Answers6

164

It seems there is no real difference

React, for some reason, attaches listeners for Component.onChange to the DOM element.oninput event. See the note in the docs on forms:

React docs - Forms

There are more people that are surprised by this behavior. For more details, refer to this issue on the React issue tracker:

Document how React's onChange relates to onInput #3964

Quote from the comments on that issue:

I don't understand why React chose to make onChange behave like onInput does. As fas as I can tell, we have no way of getting the old onChange behaviour back. Docs claim it's a "misnomer" but not it isn't really, it does fire when there's a change, just not until the input also loses focus.

For validation, sometimes we don't want to show validation errors until they're done typing. Or maybe we just don't want a re-render on every keystroke. Now the only way to do that is with onBlur but now we also need to check that the value has changed manually.

It's not that big of a deal, but it seems to me like React threw away a useful event and deviated from standard behaviour when there was already an event that does this.

I agree 100% with the comment... But I guess changing it now would bring more problems than it solves since so much code had already been written that relies on this behavior.

React is not part of the official Web API collection

Even though React is built on top of JS, and has seen a huge adoption rate, as a technology React exists to hide a whole lot of functionality under its own (fairly small) API. Once area where this is obvious is in the event system, where there's a lot going on under the surface that's actually radically different from the standard DOM event system. Not just in terms of which events do what, but also in terms of when data is allowed to persist at what stage of the event handling. You can read more about that here:

React Event System

Artur A
  • 7,115
  • 57
  • 60
Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • 2
    Final paragraph is misleading/untrue. – rounce Jul 23 '18 at 16:21
  • 1
    @rounce That's kind of a blanket statement.... Which part is misleading?? JSX *is* just sugar-coated JS (this was a statement made in comments to another answer) since it transpiles directly to JS (and you can even just write that JS manually). React *is* a framework in it's own right that does do more than just map directly to DOM... Please elaborate. – Stijn de Witt Jul 26 '18 at 09:56
  • 2
    React is not a framework (where does it provide routing, persistence, network I/O management, etc.?), it's a *library* which provides the core abstraction for manipulating display trees via platform specific backends (react-dom, react-native, etc.). – rounce Jul 26 '18 at 18:33
  • 22
    Wow ok. I sorta agree it's more a library than a framework, but to call the paragraph misleading/untrue because of that?? I also disagree with the notion that a framework should supply all aspects of a web application... There are routing frameworks for example, or things like redux which is a state management framework. In my mind, a framework lets you fill in pieces of a functioning machine. In the case of Redux, you fill in reducers. In the case of Express you fill in request handlers and middleware. Etc. But the distinction is a grey area to say the least. – Stijn de Witt Jul 26 '18 at 21:22
  • 16
    totally agree. arguing about the distinction between library and framework is really a waste of time. just depends on what level of abstraction you are working at. – swyx Aug 01 '18 at 09:37
  • The last paragraph is certainly untrue when it claims that React builds on top of JSX - JSX makes the job of writing the UI parts of a React codebase easier, but Reac itself doesn't know or care about JSX. You can write React code without it just fine. – Mike 'Pomax' Kamermans Nov 01 '18 at 18:20
  • This long winded answer and debate is tedious. Please consider the following succinct answer. https://stackoverflow.com/a/54391772/1669091 – chad steele Jan 27 '19 at 18:58
  • @StijndeWitt, wow ok, React is Javascript. Its use of input for onChange makes sense because the desired behavior is for state to be changed as the contents of the input change, not when it is unfocused like native onchange does. With everything else, you can hack React to pieces because it is JavaScript. That's what people are trying to say when they say it is a library. React leaves JavaScript naked. Yes, it adds some JSX stuff but this is syntactic sugar. Nothing like the behemoth that Angular puts on top of it. I know this is old, but no one was saying the right things. – Jordan Apr 30 '21 at 02:04
  • It's a tangent that has nothing to do with the original question – Stijn de Witt Apr 30 '21 at 07:00
37

There is no difference

React does not have the behaviour of default 'onChange' event. The 'onChange' which we see in react has the behaviour of default 'onInput' event. So to answer your question there is no difference in both of them in react. I have raised an issue on GitHub regarding the same and this is what they have to say about it:

I think that at the time this decision was made (~4 years ago?), onInput didn’t work consistently between browsers, and was confusing to people coming to the web from other platforms, as they would expect the “change” event to fire on every change. In case of React it is a bigger issue because if you fail to handle change soon enough, the controlled inputs never update, leading people to think React is broken. So the team went with calling it onChange.

In retrospect it might have been a better idea to polyfill onInput and keep its name rather than change the behavior of another event. But that ship has sailed a long time ago. We might revisit this decision in the future, but I would just encourage you to treat it as a quirk of React DOM (which you’ll get used to pretty quickly).

https://github.com/facebook/react/issues/9567

Also this article will provide more insight. As a workaround for default 'onChange' being missing, the article suggests listening to the 'onBlur' event.

https://www.peterbe.com/plog/onchange-in-reactjs

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
Nikhil Goyal
  • 576
  • 4
  • 11
  • 8
    I seem to have stumbled upon a difference. React's onChange does not trigger when selecting and replacing a character with the same character, onInput does trigger. – Per Enström Dec 16 '20 at 20:28
19

One difference seems to be that onChange is not fired when selecting and replacing a character with the same character, while onInput is.

See this sandbox: https://codesandbox.io/s/react-onchange-vs-oninput-coggf?file=/src/App.js

  • Type "A" in the field, then select all and type "B". This will trigger 4 events, 2 onChange and 2 onInput.
  • Now select all and type "B" again, this till trigger a new onInput event, but no onChange.
Per Enström
  • 902
  • 8
  • 33
  • This is the correct answer. onChange only fires when the modified text is actually different. onInput fires whenever there is an input, even if that input doesn't change the value. – superluminary Apr 19 '23 at 06:28
  • This matters if you want an action to definitely fire after input, such as focusNext or validate. – superluminary Apr 19 '23 at 07:13
12

For anyone who stumbled over this issue looking for a way to listen for the actual, DOM-based change event, this is how I did it (written in TypeScript):

import { Component, createElement, InputHTMLAttributes } from 'react';

export interface CustomInputProps {
    onChange?: (event: Event) => void;
    onInput?: (event: Event) => void;
}

/**
 * This component restores the 'onChange' and 'onInput' behavior of JavaScript.
 *
 * See:
 * - https://reactjs.org/docs/dom-elements.html#onchange
 * - https://github.com/facebook/react/issues/3964
 * - https://github.com/facebook/react/issues/9657
 * - https://github.com/facebook/react/issues/14857
 */
export class CustomInput extends Component<Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput' | 'ref'> & CustomInputProps> {
    private readonly registerCallbacks  = (element: HTMLInputElement | null) => {
        if (element) {
            element.onchange = this.props.onChange ? this.props.onChange : null;
            element.oninput = this.props.onInput ? this.props.onInput : null;
        }
    };

    public render() {
        return <input ref={this.registerCallbacks} {...this.props} onChange={undefined} onInput={undefined} />;
    }
}

Please let me know if you see ways to improve this approach or encounter problems with it. Unlike blur, the change event is also triggered when the user presses enter and is only triggered if the value actually changed.

I'm still gaining experience with this CustomInput component. For example, checkboxes behave strangely. I either have to invert event.target.checked in the onChange handler while passing the value to the checkbox with checked or get rid of this inversion when passing the value to the checkbox with defaultChecked but this then breaks that several checkboxes representing the same state in different places on the page keep in sync. (In both cases, I didn't pass an onInput handler to the CustomInput for checkboxes.)

Kaspar Etter
  • 3,155
  • 1
  • 14
  • 21
  • 4
    True life saver, thank you. Seems like React community/developers sink in this problem but they can agree how to resolve it. Backward compatibility is such sad thing, even obvious bug are preserved only because few people use the bug as feature... I had issue with ``, and `onChange` was firing even for UI navigation user need to take to select the date/time. In normal JS `onchange` was fire only once when user closed the UI - so when he finally chosen the date. This class of yours makes it work again :) – AgainPsychoX Jun 29 '21 at 08:19
  • Thank you for workaround of this react bug Have minor issue with types in react component the event, provided by this custom input differ from from "native" so gives an errors: onChange={ e => fn(e.currentTarget.value) } 'e.currentTarget' is possibly 'null'. Property 'value' does not exist on type 'EventTarget'. – Poul Jan 08 '23 at 08:23
9

As you can see in various comments here, React treats onChange and onInput the same and so, rather than debate the merits of this decision. Here's the solution.

Use onBlur when you don't want to process the user's edits until they're done. :)

chad steele
  • 828
  • 9
  • 13
4

Recently I got a bug where onChange would not allow copy and paste in the input field on IE11. Whereas the onInput event would allow that behavior. I could not find any documentation that would describe this in the docs, but that does show there is a difference between the two (expected or not).

Alan Souza
  • 7,475
  • 10
  • 46
  • 68