Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how React's onChange relates to onInput #3964

Closed
graue opened this issue May 27, 2015 · 20 comments
Closed

Document how React's onChange relates to onInput #3964

graue opened this issue May 27, 2015 · 20 comments

Comments

@graue
Copy link
Contributor

graue commented May 27, 2015

It would be nice for the Forms doc to be more explicit about the fact that React's onChange supersedes, and should generally be used in place of, the DOM's built-in onInput event. People might be used to using onInput instead for text inputs and textareas, since, with the raw DOM, the change event for these controls doesn't fire until the control loses focus.

@AnSavvides
Copy link
Contributor

Opened a PR to address this: #4003

AnSavvides pushed a commit to AnSavvides/react that referenced this issue Jun 7, 2015
graue added a commit that referenced this issue Jun 9, 2015
[#3964] Add note about React's onChange vs. DOM's oninput
graue added a commit that referenced this issue Jun 10, 2015
[#3964] Add note about React's onChange vs. DOM's oninput
@AnSavvides
Copy link
Contributor

Merged, hurray, we can close this now! 🎉

@graue graue closed this as completed Jun 11, 2015
rocketraman added a commit to ritzyed/ritzy that referenced this issue Aug 27, 2015
The React onChange event "supersedes" the native DOM "onInput" event. See
facebook/react#3964.
@sunyang713
Copy link

wait but why?

@mnpenner
Copy link
Contributor

mnpenner commented May 16, 2016

Yeah...why? 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 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.

@evpozdniakov
Copy link

evpozdniakov commented May 23, 2016

Agree with mnpenner.

With onChange fireing on every keystroke, my redux store changes simultaneously. To avoid that, I have to use onBlur.

Finally, we have two events - onInput and onChange - which work same manner.

@piotr-cz
Copy link

piotr-cz commented Dec 2, 2016

Besides there is no easy workaround to replicate such behaviour for the <input type="range" /> element.

Native events behave such way that moving the slider around triggers an onInput event and releasing it triggers onChange.

@bcherny
Copy link

bcherny commented Aug 31, 2017

Another note: Autofill events (at least on Chrome/OSX) trigger onInput, but not onChange! So if I want to capture a change event to an input that might be filled using Chrome's autofill feature, I need to bind to both onInput (to detect keystrokes and autofill) and onChange (to placate React [1]).

[1]

Warning: Failed form propType: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly. Check the render method of StatelessComponent.

@ddelrio1986
Copy link

@mnpenner, @evpozdniakov for validation messages or things that you want to wait until they stop typing for use debounce in your event handler instead of waiting until onBlur.

@fabiosantoscode
Copy link

Still no way of using onChange? I've been writing lots of logic to get around not having a real change event.

I think to be a better abstraction, React needs to stick to onChange and let us use onInput without a silly warning.

Validation has never been this hard.

@graue
Copy link
Contributor Author

graue commented Feb 14, 2019

For better or worse, this has been the behavior for quite a while, and many React users rely on it.

IMHO, it's probably too late in the game to totally change what "onChange" means in React. But if you feel strongly, maybe do a quick PR to propose a solution with a sensible upgrade path? (Before spending a lot of time on it, get the thoughts of the core team.)

@fabiosantoscode
Copy link

Right @graue, I've suggested #14857 instead to create a new event and minimise breaking changes.

@alamothe
Copy link

Definitely a huge design issue with React. Hope it doesn't stay this way forever

@qtv100291
Copy link

This is a mistake or a feature :/

@milessorce
Copy link

This is a mistake or a feature :/

Features are supposed to be useful... this is the opposite of useful.

@artium-ryanh
Copy link

For anyone who needs a workaround to get true onChange behavior, you can implement your own component to use HTML's built-in onchange event. Here's how I've done it, inspired by Kaspar Ettar's solution here. Kaspar's allows you to use both onchange and oninput, mine is just for onchange.

import { Component, InputHTMLAttributes } from 'react';

export interface OnChangeInputProps {
    onChange?: (target: HTMLInputElement) => void;
}

/**
 * This component restores the 'onChange' 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 OnChangeInput extends Component<Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'onInput' | 'ref'> & OnChangeInputProps> {
    private readonly registerCallbacks = (element: HTMLInputElement | null) => {
        if (element) {
            const onChange: (event: Event) => void = (event) => {
                const target = (event.currentTarget!) as HTMLInputElement;
                this.props.onChange!(target);
            };
            element.onchange = this.props.onChange ? onChange : null;
        }
    };

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

@dijonkitchen
Copy link

Looks like there is an official answer now:

https://reactjs.org/docs/dom-elements.html#onchange

@mnpenner
Copy link
Contributor

mnpenner commented Apr 6, 2022

@dijonkitchen I don't think that's new. See #3964 (comment)

rocketnova added a commit to navapbc/wic-mt-demo-project-eligibility-screener that referenced this issue Oct 15, 2022
onChange

Note that onChange() was causing errors where the text field and text
area inputs were failing to trigger on first deletion after refresh.
Switching to onInput() fixed the issue. See
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event.
However, facebook/react#3964 seems to indicate
that react *should* be treating onInput() the same as onChange(). Proof
of concept (see `src/pages/testChange.tsx`) proves otherwise. Something
in our particular setup, almost certainly to do with the way we are
loading the initial state value from session/local storage, in
combination with onChange() causes it to have buggy behavior.
rocketnova added a commit to navapbc/wic-mt-demo-project-eligibility-screener that referenced this issue Oct 18, 2022
onChange

Note that onChange() was causing errors where the text field and text
area inputs were failing to trigger on first deletion after refresh.
Switching to onInput() fixed the issue. See
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event.
However, facebook/react#3964 seems to indicate
that react *should* be treating onInput() the same as onChange(). Proof
of concept (see `src/pages/testChange.tsx`) proves otherwise. Something
in our particular setup, almost certainly to do with the way we are
loading the initial state value from session/local storage, in
combination with onChange() causes it to have buggy behavior.
@andrechalella
Copy link

It is really aggravating, this "official answer". How presumptuous of them to call the HTML Spec official and consolidated event name a "misnomer".

@jimmythecoder
Copy link

2024 and issue still exists, this one wasted a serious chunk of time, atleast I now have an answer but if I didnt trust facebook before I sure dont trust them now. Always follow the spec, there should be no alternative breaking change or not.

@lolloz98
Copy link

lolloz98 commented Jul 16, 2024

Guys for people looking to do it with functions, expanding on Ettar solution, this seems to be working (here I am using MUI component, but input component works as well):

import { TextField } from "@mui/material";

export default function MyCustomInput() {
    const registerCallbacks  = (element: HTMLInputElement | null) => {
        if (element) {
            element.onchange = (e) => console.log("onChange", (e.target as HTMLInputElement).value);
            element.oninput = (e) => console.log("onInput");
        }
    };

    return (
        <TextField label="Outlined" variant="outlined" inputProps={{ref:registerCallbacks, onChange: undefined, onInput: undefined}} />
    );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.