在 React 中,onChange 和 onInput 有什么区别?

In React, what's the difference between onChange and onInput?

我已经尝试四处寻找这个问题的答案,但大多数都在 React 的上下文之外,其中 onChange 在模糊时触发。

在执行各种测试时,我似乎无法分辨这两个事件有何不同(应用于文本区域时)。任何人都可以阐明这一点吗?

好像没什么区别

出于某种原因,React 将 Component.onChange 的侦听器附加到 DOM element.oninput 事件。请参阅表单文档中的注释:

React docs - Forms

有更多人对这种行为感到惊讶。有关详细信息,请参阅 React 问题跟踪器上的此问题:

Document how React's onChange relates to onInput #3964

引用该问题的评论:

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.

我 100% 同意评论...但我想现在更改它会带来比它解决的问题更多的问题,因为已经编写了如此多的代码依赖于此行为。

React 不属于官方网站 API 合集

尽管 React 建立在 JS 之上,并且采用率很高,但作为一种技术,React 的存在是为了在其自身(相当小)下隐藏大量功能 API。一旦这一点在事件系统中变得明显,表面下发生的很多事情实际上与标准 DOM 事件系统完全不同。不仅在哪些事件做什么方面,而且在允许数据在事件处理的哪个阶段保留的时间方面。您可以在此处阅读更多相关信息:

React Event System

没有区别

React 没有默认 'onChange' 事件的行为。我们在 React 中看到的 'onChange' 具有默认 'onInput' 事件的行为。因此,要回答您的问题,两者的反应没有区别。我已经在 GitHub 上提出了一个关于相同问题的问题,这是他们必须说的:

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

此外,本文将提供更多见解。作为缺少默认 'onChange' 的解决方法,文章建议收听 'onBlur' 事件。

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

最近我遇到一个错误,onChange 不允许在 IE11 的输入字段中复制和粘贴。而 onInput 事件将允许该行为。我在文档中找不到任何可以描述这一点的文档,但这确实表明两者之间存在差异(预期与否)。

正如您在此处的各种评论中看到的那样,React 对 onChange 和 onInput 的处理方式相同,而不是争论这个决定的优点。这是解决方案。

如果您不想在用户的编辑完成之前对其进行处理,请使用 onBlur。 :)

对于任何偶然发现这个问题并寻找一种方法来监听实际的、基于 DOM 的 change 事件的人,我就是这样做的(用 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} />;
    }
}

如果您发现改进此方法的方法或遇到问题,请告诉我。与 blur 不同的是,change 事件也会在用户按下回车键时触发,并且只有在值实际发生变化时才会触发。

我仍在积累使用此 CustomInput 组件的经验。例如,复选框的行为很奇怪。在使用 checked 将值传递给复选框时,我必须在 onChange 处理程序中反转 event.target.checked ,或者在使用 defaultChecked 将值传递给复选框时摆脱这种反转但这会破坏页面上不同位置代表相同状态的几个复选框保持同步。 (在这两种情况下,我都没有将 onInput 处理程序传递给复选框的 CustomInput。)

一个区别似乎是 onChange 在 select 并用相同的字符替换一个字符时不会触发,而 onInput 是。

查看此沙盒:https://codesandbox.io/s/react-onchange-vs-oninput-coggf?file=/src/App.js

  • 在字段中键入“A”,然后 select 全部并键入“B”。这将触发 4 个事件,2 onChange 和 2 onInput.
  • 现在 select 所有并再次键入“B”,直到触发新的 onInput 事件,但没有 onChange.

不同之处在于 oninput 事件在元素值更改后立即发生,而 onchange 事件在元素失去焦点时发生,在内容更改后发生。