为啥在 dispatchEvent 上不调用 React 事件处理程序?

Posted

技术标签:

【中文标题】为啥在 dispatchEvent 上不调用 React 事件处理程序?【英文标题】:Why React event handler is not called on dispatchEvent?为什么在 dispatchEvent 上不调用 React 事件处理程序? 【发布时间】:2016-12-28 03:32:39 【问题描述】:

考虑一下 React 组件中的以下输入元素:

<input onChange=() => console.log('onChange') ... />

在测试 React 组件时,我正在模拟用户更改输入值:

input.value = newValue;
TestUtils.Simulate.change(input);

这会按预期导致'onChange' 被记录。

但是,当'change' 事件被直接调度时(我使用的是jsdom):

input.value = newValue;
input.dispatchEvent(new Event('change'));

onChange 处理程序未被调用。

为什么?

我使用dispatchEvent 而不是TestUtils.Simulate 的动机是因为TestUtils.Simulate 不支持事件冒泡,而我的组件的行为依赖于此。我想知道是否有办法在没有TestUtils.Simulate 的情况下测试事件?

【问题讨论】:

【参考方案1】:

没有ReactTestUtils.Simulate的一种方法:

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://unpkg.com/react-trigger-change/dist/react-trigger-change.js';
document.head.appendChild(script);
input.value = value;
reactTriggerChange(input);

查看source of react-trigger-change 以挑选所需的内容。示例 sn-p:

if (nodeName === 'select' ||
    (nodeName === 'input' && type === 'file')) 
    // IE9-IE11, non-IE
    // Dispatch change.
    event = document.createEvent('htmlEvents');
    event.initEvent('change', true, false);
    node.dispatchEvent(event);
  
https://github.com/vitalyq/react-trigger-change https://github.com/facebook/react/issues/3249#issuecomment-292919084

【讨论】:

你是我的救命恩人! 没有足够的信息,在这里。 value 是什么?如果我想单击一个按钮,应该将其添加到const input 还是const input.value?所以我试着把它设置为const input,得到了reactTriggerChange is undefined,所以我添加了一个导入语句。然后得到Cannot read property 'toLowerCase' of undefined。另外,我有大量的 linting 错误,我必须清理才能使用这个包:( . @vapcguy 添加了更多描述 @rofrol 谢谢-我将最后 3 行拉出,以便在我为一个按钮单击另一个按钮而制作的事件处理程序中使用它们。我分配nodeconst node = document.getElementsByClassName("close"); 我的目标按钮有class="close" & 它是页面上唯一具有该类的元素。当我单击包含这些代码行的第一个按钮时,给我Uncaught TypeError: Cannot read property '__reactInternalInstance$iedoafjkjst1cmk11d5tgldi' of undefined - 与TestUtils.Simulate.click(node); 类似的错误***.com/questions/36752434/… @rofrol 我认为我的问题在于我如何获得node。当我分配一个 ID 并使用const node = document.getElementById("myID") 时,我得到了TestUtils.Simulate.click(node); 终于可以工作了。我再次尝试使用上面使用initEventdispatchEvent 的代码,它停止给我错误,但它也不起作用——什么也没发生。反应版本 15.3.1。我不知道。【参考方案2】:

React 使用自己的带有SyntheticEvents 的事件系统(防止浏览器不兼容并给予 React 更多的事件控制权)。

正确使用TestUtils 会创建这样一个事件,该事件将触发您的onChange 侦听器。

另一方面,dispatchEvent 函数将创建一个“本机”浏览器事件。但是您拥有的事件处理程序是由 react 管理的,因此只会对 reacts SyntheticEvents 作出反应(错误)。

你可以在这里阅读反应事件:https://facebook.github.io/react/docs/events.html

【讨论】:

【参考方案3】:

我为输入元素创建了一个小版本的https://github.com/vitalyq/react-trigger-change。

没有外部依赖,复制粘贴就可以了。

适用于 React 16.9,经过 Safari (14)、Chrome (87) 和 Firefox (72) 验证。

const triggerInputChange = (node: HTMLInputElement, inputValue: string) => 
      const descriptor = Object.getOwnPropertyDescriptor(node, 'value');

      node.value = `$inputValue#`;
      if (descriptor && descriptor.configurable) 
        delete node.value;
      
      node.value = inputValue;

      const e = document.createEvent('HTMLEvents');
      e.initEvent('change', true, false);
      node.dispatchEvent(e);

      if (descriptor) 
        Object.defineProperty(node, 'value', descriptor);
      
;

【讨论】:

&lt;input type='checkbox' /&gt; 怎么样,我将value 替换为“已检查”但不起作用

以上是关于为啥在 dispatchEvent 上不调用 React 事件处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 vue 3 watch 上不调用对象突变?

Chrome 中的 dispatchEvent,但 IE11 中没有

IE中的fireEvent和webkit中的dispatchEvent

动态 HTML 加 window.onload 在 IE 上不起作用

为啥这个 $.post() 在 iOS 上不起作用?

为啥 AUNetSend 在 iOS 上不可用? (或任何替代建议)