在js中触发onchange事件的最好方法是什么?

美好的一天。

我们使用backbone + reactjs包来构build客户端应用程序。 严重依赖臭名昭着的valueLink我们通过自己的包装器直接向模型传递值,支持反应js接口的双向绑定。

现在我们面临这个问题:

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

看来,React有很多事件处理策略取决于浏览器。 有什么常见的方法来触发特定DOM元素的更改事件,以便React能够听到它?

至less在文本input上,看起来onChange正在监听input事件:

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

我知道这个答案有点晚,但我最近面临类似的问题。 我想触发一个嵌套组件的事件。 我有一个无线电和checkboxtypes小部件(他们是行为像checkbox和/或单选button)的列表,并在应用程序的其他地方,如果有人closures工具箱,我需要取消选中一个。

我发现了一个非常简单的解决scheme,不知道这是否是最佳实践,但它的工作原理。

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

这触发了domNode上的点击事件,我的处理程序通过反应连接确实被调用,所以它的行为就像我所期望的,如果有人点击元素。 我没有testingonChange,但它应该工作,并不知道如何这将公平的真正的老版本的IE浏览器,但我相信至lessIE9和支持MouseEvent。

因为我的组件非常小(我的应用程序中只有一部分被使用,因为我还在学习它),所以我最终从这个特殊的用例中移开了,我可以通过另一种方式实现同​​样的事情,而不需要引用dom节点。

更新:

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

对于React 16和React> = 15.6

Setter .value=不能正常工作,因为React库会覆盖input值设置器,但我们可以直接在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元素,你应该使用HTMLTextAreaElement类的prototype

新的codepen示例 。

所有信贷给这个贡献者和他的解决scheme

仅适用于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

要理解为什么需要新标志我发现这个评论非常有帮助:

React中的input逻辑现在重复数据删除的变化事件,所以它们不会每个值触发一次以上。 它监听浏览器onChange / onInput事件以及设置DOM节点值prop(当你通过javascript更新值)。 这有副作用,如果你手动更新input的值input.value ='foo'然后用{target:input}分派一个ChangeEvent React将注册set和事件,看它的值仍然是''foo ',认为这是一个重复的事件,并吞下去。

这在正常情况下工作正常,因为一个“真正的”浏览器启动的事件不触发element.value集。 您可以通过用模拟标记标记您触发的事件来暗中摆脱这种逻辑,反应将始终触发事件。 https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128

您可以使用ReactTestUtils来模拟事件,但这是为unit testing而devise的。

我build议不要在这种情况下使用valueLink,只是简单地听取插件触发的更改事件,并更新input的状态作为响应。 双向绑定的应用程序更像是一个演示,而不是其他任何东西; 它们只是为了强调纯双向绑定不适用于大多数应用程序,而且通常需要更多的应用程序逻辑来描述应用程序中的交互。

在任意元素上触发更改事件会造成组件之间的依赖关系,这是很难推理的。 坚持使用React的单向数据stream更好。

没有简单的片段来触发React的变化事件。 该逻辑在ChangeEventPlugin.js中实现,不同的inputtypes和浏览器有不同的代码分支。 此外,不同版本的React的实现细节也不尽相同。

我已经构build了反应触发器更改来完成这个任务,但是它的目的是用于testing,而不是作为生产依赖项:

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

CodePen

如果您使用Backbone和React,我会推荐以下其中一项,

  • Backbone.React.Component
  • react.backbone

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

既然我们使用函数来处理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)} /> ); } } 

扩展Grin / Dan Abramov的答案,这个工作可以跨多种inputtypes。 testingReact> = 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); } };