在react js中触发onchange事件的最佳方法是什么

wal*_*ice 87 reactjs

我们使用Backbone + ReactJS包来构建客户端应用程序.严重依赖臭名昭着的valueLink我们通过自己的包装器将值直接传播到模型,该包装器支持ReactJS接口以进行双向绑定.

现在我们遇到了这个问题:

我们有jquery.mask.js插件以编程方式格式化输入值,因此它不会触发React事件.所有这些都会导致模型从用户输入接收未格式化的值并从插件中忽略格式化的值时导致这种情况.

React似乎有很多依赖于浏览器的事件处理策略.是否有任何常见的方法来触发特定DOM元素的更改事件,以便React会听到它?

Gri*_*rin 114

对于React 16和React> = 15.6

Setter .value=不能正常工作,因为React库会覆盖输入值设置器,但我们可以直接在inputas上下文中调用该函数.

var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');

var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);
Run Code Online (Sandbox Code Playgroud)

对于textarea元素,你应该使用prototypeHTMLTextAreaElement类.

新的codepen示例.

所有归功于此贡献者他的解决方案

只有React <= 15.5的过期答案

有了react-dom ^15.6.0您可以使用simulated标志事件对象的事件经过

var ev = new Event('input', { bubbles: true});
ev.simulated = true;
element.value = 'Something new';
element.dispatchEvent(ev);
Run Code Online (Sandbox Code Playgroud)

用一个例子做了一个codepen

要理解为什么需要新标志,我发现此评论非常有用:

React中的输入逻辑现在重复数据删除的更改事件,因此它们不会每个值触发多次.它侦听浏览器onChange/onInput事件以及DOM节点值prop上的设置(当您通过javascript更新值时).这有一个副作用意味着如果你手动更新输入的值input.value ='foo'然后用{target:input}调度一个ChangeEvent,React将注册set和event,看它的值仍然是`'foo ',将它视为重复事件并吞下它.

这在正常情况下工作正常,因为"真正的"浏览器启动事件不会触发element.value上的集合.您可以通过使用模拟标记标记触发的事件来秘密地避开此逻辑,并且反应将始终触发事件. https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128

  • 谢谢,这确实在react-dom ^15.6.0中起作用。但在 React 16.0 后,这似乎已经停止工作了。关于使用模拟标志触发更改事件的替代方案有什么想法吗? (3认同)

Mic*_*ael 60

至少在文本输入上,似乎onChange正在侦听输入事件:

var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
Run Code Online (Sandbox Code Playgroud)

  • 这不再适用于react-dom ^ 15.6.0 (9认同)
  • React [不再支持 IE8](https://facebook.github.io/react/blog/2016/01/12/discontinuing-ie8-support.html)。对于 IE9,您可以使用类似 `var event = document.createEvent('CustomEvent'); 之类的东西。event.initCustomEvent('input', true, false, { });`,但我手边没有 IE9 VM。 (2认同)

Rob*_*t-W 19

我知道这个答案有点迟,但我最近遇到了类似的问题.我想在嵌套组件上触发事件.我有一个带收音机和复选框类型小部件的列表(它们的行为类似于复选框和/或单选按钮),在应用程序的其他位置,如果有人关闭了工具箱,我需要取消选中一个.

我找到了一个非常简单的解决方案,不确定这是否是最佳实践但是有效.

var event = new MouseEvent('click', {
 'view': window, 
 'bubbles': true, 
 'cancelable': false
});
var node = document.getElementById('nodeMyComponentsEventIsConnectedTo');
node.dispatchEvent(event);
Run Code Online (Sandbox Code Playgroud)

这触发了domNode上的click事件,并且我通过react连接的处理程序确实被调用,因此如果有人点击该元素,它的行为就像我期望的那样.我没有测试过onChange但它应该可以工作,并且不确定这在真正的旧版IE中如何公平,但我相信至少在IE9及以上版本中支持MouseEvent.

我最终因为我的特定用例而离开了这个,因为我的组件非常小(我的应用程序中只有一部分用了反应,因为我还在学习它)而且我可以通过另一种方式实现相同的功能,而无需引用dom节点.

更新:

正如其他人在评论中所述,最好使用它this.refs.refname来获取对dom节点的引用.在这种情况下,refname是您通过组件附加的引用<MyComponent ref='refname' />.

  • >您可以使用React.findDOMNode函数代替ID.或者在你的元素中添加一个`ref`然后使用`this.ref.refName.dispatchEvent` (2认同)

cjp*_*ete 11

扩展 Grin/Dan Abramov 的答案,这适用于多种输入类型。在 React >= 15.5 中测试

const inputTypes = [
    window.HTMLInputElement,
    window.HTMLSelectElement,
    window.HTMLTextAreaElement,
];

export const triggerInputChange = (node, value = '') => {

    // only process the change on elements we know have a value setter in their constructor
    if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {

        const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
        const event = new Event('input', { bubbles: true });

        setValue.call(node, value);
        node.dispatchEvent(event);

    }

};
Run Code Online (Sandbox Code Playgroud)

  • 不适用于选择元素。对于事件,您需要“更改”而不是“输入”。 (5认同)

Sop*_*ert 9

您可以使用ReactTestUtils模拟事件,但这是为单元测试而设计的.

我建议不要在这种情况下使用valueLink,只是监听插件触发的更改事件并更新输入的状态作为响应.双向绑定更像是一个演示而不是其他任何东西; 它们仅包含在插件中,以强调纯双向绑定不适合大多数应用程序,并且通常需要更多应用程序逻辑来描述应用程序中的交互.


oct*_*ron 9

对于HTMLSelectElement,即<select>

var element = document.getElementById("element-id");
var trigger = Object.getOwnPropertyDescriptor(
  window.HTMLSelectElement.prototype,
  "value"
).set;
trigger.call(element, 4); // 4 is the select option's value we want to set
var event = new Event("change", { bubbles: true });
element.dispatchEvent(event);
Run Code Online (Sandbox Code Playgroud)


小智 7

我今天偶然发现了同样的问题。虽然'click', 'focus', 'blur'JavaScript 中默认支持开箱即用的事件,但其他有用的事件'change', 'input'(例如)尚未实现。

我想出了这个通用解决方案,并根据接受的答案重构了代码。

export const triggerNativeEventFor = (elm, { event, ...valueObj }) => {
  if (!(elm instanceof Element)) {
    throw new Error(`Expected an Element but received ${elm} instead!`);
  }

  const [prop, value] = Object.entries(valueObj)[0] ?? [];
  const desc = Object.getOwnPropertyDescriptor(elm.__proto__, prop);

  desc?.set?.call(elm, value);
  elm.dispatchEvent(new Event(event, { bubbles: true }));
};
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?

triggerNativeEventFor(inputRef.current, { event: 'input', value: '' });
Run Code Online (Sandbox Code Playgroud)

您在'event'键值对之后传递的任何第二个属性都将被考虑在内,其余的将被忽略/丢弃。故意这样写是为了不混乱辅助函数的参数定义。为什么不默认'value'仅获取描述符的原因是,例如,如果您有一个本机复选框<input type="checkbox" />,那么它没有值而不是'checked'道具/属性。然后你可以通过你想要的检查状态,如下所示:

triggerNativeEventFor(checkBoxRef.current, { event: 'input', checked: false });
Run Code Online (Sandbox Code Playgroud)


spe*_*.sm 7

onChange有效的解决方案可能在一定程度上取决于您尝试触发的函数的实现。对我有用的方法是访问附加到 DOM 元素的 React props 并直接调用该函数。

我创建了一个辅助函数来获取反应道具,因为它们带有像这样的哈希后缀.__reactProps$fdb7odfwyz

它可能不是最强大的,但很高兴知道它是一个选择。

function getReactProps(el) {
  const keys = Object.keys(el);
  const propKey = keys.find(key => key.includes('reactProps'));
  return el[propKey];
}

const el = document.querySelector('XX');
getReactProps(el).onChange({ target: { value: id }  });
Run Code Online (Sandbox Code Playgroud)

由于仅使用 onChange 函数,target.value我可以传递一个简单的对象来onChange触发我的更改。


此方法还可以帮助处理顽固的 React 拥有的 DOM 元素,这些元素会列出onMouseDown但不会.click()像您期望的那样响应。

getReactProps(el).onMouseDown(new Event('click'));
Run Code Online (Sandbox Code Playgroud)