首页 > 解决方案 > 隐藏的输入不会触发 React 的 onChange 事件

问题描述

我在 React 的输入字段中遇到了一个奇怪的问题。我知道hidden输入不会触发inputchange事件的事实。但是,即使我手动触发它们,React 的onChange事件仍然没有被调用。

我在这里触发了changeinput事件,因为 ReactonChange实际上是input事件。当我在inputRef( addEventListener("change", () => { ... })) 上设置一个事件侦听器以进行测试时,它被毫无问题地调用。然而,事实证明 React 在拦截它时遇到了一些问题。

这是我当前的代码:

const [fieldValue, setFieldValue] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);

const handleClick = useCallback((): void => {
    if (inputRef.current) {
        inputRef.current.dispatchEvent(new Event("change", { bubbles: true }));
        inputRef.current.dispatchEvent(new Event("input", { bubbles: true }));
    }

    setFieldValue(prev => prev + 1);
}, []);

JSX:

<input type="hidden" ref={inputRef} value={fieldValue} onChange={(e) => { console.log("React:onChange"); }} />
<button type="button" onClick={handleClick}>Hit it</button>

我在这里做错什么了吗?我还需要做什么才能正确触发 React 的onChange事件?

标签: javascriptreactjstypescriptdom-events

解决方案


React 中的事件由 react-dom 通过不同的插件处理。输入事件由SimpleEventPlugin管理(参见https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SimpleEventPlugin.js

这个插件获取一个事件并重新调度它而不会干扰太多。因此,您可以将其作为原生事件进行调度,并且无需太多更改即可作为SyntheticEvent触发。

更改事件由ChangeEventPlugin处理(请参阅https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ChangeEventPlugin.js)。这个插件有这个目的:

这个插件创建了一个onChange事件来规范跨表单元素的更改事件。此事件在可以更改元素的值而不会看到闪烁的时候触发。

该插件会触发更改事件,但不完全与本机更改事件一样。例如,文本输入元素上的本机更改事件仅在模糊时触发。请参阅https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event

对于某些元素,包括 ,在控件失去焦点之前不会触发 change 事件。

但是对于 React,它的触发方式不同,每次值的变化。为此,它由具有以下限制的插件处理:该事件将仅在以下输入类型上触发:

  color: true,
  date: true,
  datetime: true,
  'datetime-local': true,
  email: true,
  month: true,
  number: true,
  password: true,
  range: true,
  search: true,
  tel: true,
  text: true,
  time: true,
  url: true,
  week: true

所以在隐藏的输入上,react 会停止调度。

另一个限制是只有当输入的值实际发生变化时才会调度事件。看

 if (updateValueIfChanged(targetNode)) {
    return targetInst;
  }

因此,即使在受支持的输入上,您的调度也不会通过插件。您可以在此代码段中看到它,通过操作用于获取输入值的方法,您可以设法调度事件。

class Hello extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();

  }

  componentDidMount() {
  //_valueTracker.getValue is used for comparing the values of the input. If you write fake value in the input it'll dispatch, anything else won't
    document.getElementsByTagName('INPUT')[0]._valueTracker.getValue = () => {
      return 'fake value'
    }
  }

  render() {

    const handleClick = () => {
      if (this.myRef) {

        this.myRef.current.dispatchEvent(new Event("change", {
          bubbles: true
        }));


      }
    };

    return <div > < input type = "text"
    ref = {
      this.myRef
    }
    onChange = {
      (e) => {
        console.log("React:onChange");
      }
    }
    /><button type="button"  onClick={handleClick}>Hit it</button > < /div>

  }
}

ReactDOM.render( <
  Hello name = "World" / > ,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="container">

</div>

所以对于隐藏的输入,没有办法让它工作(注意是符合标准的)。对于文本(或其他支持的输入),您可以但您需要或多或少地破解比较事件之前的值与事件之后的值的方法。


推荐阅读