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

时间:2014-05-27 14:49:20

标签: reactjs

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

现在我们遇到了问题:

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

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

12 个答案:

答案 0 :(得分:92)

对于React 16和React> = 15.6

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

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);

对于textarea元素,您应该使用prototype类的HTMLTextAreaElement

codepen example

this contributorhis solution

的所有赠送金额

仅针对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);

我做了codepen with an example

为了理解为什么需要新标志,我发现this comment非常有帮助:

  

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

     

这在正常情况下工作正常,因为启动了“真正的”浏览器   event不会触发element.value上的set。你可以保释出来   这个逻辑秘密地通过标记您用模拟触发的事件   标志和反应总是会触发事件。   https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128

答案 1 :(得分:60)

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

var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);

答案 2 :(得分:19)

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

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

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

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

我最终因为我的特定用例而离开了这个,因为我的组件非常小(我的应用程序中只有一部分用了反应,因为我还在学习它)而且我可以用另一种方式实现同​​样的东西而不需要引用dom节点。

更新:

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

答案 3 :(得分:8)

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

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

答案 4 :(得分:3)

在任意元素上触发更改事件会在组件之间创建难以推理的依赖关系。坚持使用React的单向数据流更好。

没有简单的代码片段可以触发React的更改事件。逻辑在ChangeEventPlugin.js中实现,并且针对不同的输入类型和浏览器存在不同的代码分支。此外,实施细节因React版本而异。

我已经构建了react-trigger-change来完成这项任务,但它旨在用于测试,而不是用作生产依赖:

let node;
ReactDOM.render(
  <input
    onChange={() => console.log('changed')}
    ref={(input) => { node = input; }}
  />,
  mountNode
);

reactTriggerChange(node); // 'changed' is logged

CodePen

答案 5 :(得分:2)

扩展Grin / Dan Abramov的答案,这适用于多种输入类型。在React&gt; = 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);

    }

};

答案 6 :(得分:1)

因为我们使用函数来处理onchange事件,所以我们可以这样做:

class Form extends Component {
 constructor(props) {
  super(props);
  this.handlePasswordChange = this.handlePasswordChange.bind(this);
  this.state = { password: '' }
 }

 aForceChange() {
  // something happened and a passwordChange
  // needs to be triggered!!

  // simple, just call the onChange handler
  this.handlePasswordChange('my password');
 }

 handlePasswordChange(value) {
 // do something
 }

 render() {
  return (
   <input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} />
  );
 }
}

答案 7 :(得分:1)

我在React的Github问题上发现了这一点:像护身符(v15.6.2)

这是我实现文本输入的方式:

changeInputValue = newValue => {

    const e = new Event('input', { bubbles: true })
    const input = document.querySelector('input[name=' + this.props.name + ']')
    console.log('input', input)
    this.setNativeValue(input, newValue)
    input.dispatchEvent(e)
  }

  setNativeValue (element, value) {
    const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set
    const prototype = Object.getPrototypeOf(element)
    const prototypeValueSetter = Object.getOwnPropertyDescriptor(
      prototype,
      'value'
    ).set

    if (valueSetter && valueSetter !== prototypeValueSetter) {
      prototypeValueSetter.call(element, value)
    } else {
      valueSetter.call(element, value)
    }
  }

答案 8 :(得分:1)

事件类型input<select>上对我不起作用,但将其更改为change有用

useEffect(() => {
    var event = new Event('change', { bubbles: true });
    selectRef.current.dispatchEvent(event); // ref to the select control
}, [props.items]);

答案 9 :(得分:0)

如果您使用的是Backbone和React,我建议使用以下其中一种,

它们都有助于将Backbone模型和集合与React视图集成在一起。您可以像使用Backbone视图一样使用Backbone事件。我已经涉足过两者并没有看到太大的区别,除了一个是mixin而另一个是React.createClass改为React.createBackboneClass

答案 10 :(得分:0)

对于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);

答案 11 :(得分:0)

这个丑陋的解决方案对我有用:

let ev = new CustomEvent('change', { bubbles: true });
Object.defineProperty(ev, 'target', {writable: false, value: inpt });
Object.defineProperty(ev, 'currentTarget', {writable: false, value: inpt });
const rHandle = Object.keys(inpt).find(k => k.startsWith("__reactEventHandlers"))
inpt[rHandle].onChange(ev);