React 奇技淫巧 - defaultValue 和虚拟 dom diff 算法实现表单重
我们知道 React 的标准模式是单向数据流,而其表单项通常需要监听 onChange 事件,然后通过改变外部的 value 来回写表单项的 value,譬如如下 input class App extends React.Component { constructor( props ) { super( props ); this.state = { inputValue: 'default' } this.inputChangeHandler = ( e )=>{ this.setState( { inputValue: e.target.value } ); } } render() { return ( <div> <form> <input value={ this.state.inputValue } onChange={ this.inputChangeHandler } /> </form> </div> ) } } 如果表单有很多表单项,那么这种标准的做法需要你写很多个 state 的属性和很多个 onChange 监听函数,这是一个体力活儿。但是一般的表单应用其实不需要实时监控表单项的用户输入,用 defaultValue 足以,在表单项目 onBlur 或者最后提交的时候一次验证获取用户输入即可,譬如: class App extends React.Component { constructor( props ) { super( props ); this.submit = ( e )=>{ let userInputValue = this.refs.userInput.value; // 1. 验证 userInputValue // 2. 提交表单 } } render() { return ( <div> <form> <input ref="userInput" defaultValue="default" /> <button onClick={ this.submit }>提交</button> </form> </div> ) } } 这样就可以少写不少代码,当然你可以写一些工具去批量添加所有的 onChange 事件监听函数和对应的 state 的属性,譬如 redux-form。(回头一想,这种写法在提交时候也需要写很多获取用户输入的代码,如果使用第一种正模式,那么提交时候只需要获取 state 就可以了,不过这里先不讨论这些)<br/> class App extends React.Component { constructor( props ) { super( props ); // fieldSetWrapperType 是一个标志位属性,render 中会根据这个变量的值的不同,渲染不同的元素 this.fieldSetWrapperType = 'div'; this.submit = ( e )=>{ let userInputValue = this.refs.userInput.value; // 1. 验证 userInputValue // 2. 提交表单 } this.reset = ()=>{ // 点击重置,改变标志位 this.fieldSetWrapperType = this.fieldSetWrapperType === 'div' ? 'section' : 'div'; // 强制刷新这个组件 this.forceUpdate(); } } // 把表单项的渲染抽象到一个方法中,避免重复编码 renderFieldSet() { return ( <input ref="userInput" defaultValue="default" /> ); } render() { return ( <div> <form> { /* 根据 fieldSetWrapperType 值的不同,渲染不同的元素(表单项的 wrapper 元素) */ this.fieldSetWrapperType === 'div' ? <div className="wrapper">{ this.renderFieldSet() }</div> : <section className="wrapper">{ this.renderFieldSet() }</section> } <button onClick={ this.submit }>提交</button> <button onClick={ this.reset }>重置</button> </form> </div> ) } } 思路就是在表单项外面包一层元素,每次点击重置后改变一个变量,再强制刷新这个组件,组件根据这个变量不同的值把这个包装元素的 type 改变,那么它下面的所有表单项的虚拟 dom 都会被重新创建,达到了重置的目的。不过这个效果依赖于 React 虚拟dom diff 算法。如果以后算法改变了,那么可能就失效了,而且这个写法是反模式的,我初衷是想在处理巨型表单时候少写点代码偷懒用。如果使用时候出现什么副作用,鄙人概不负责。 此技巧在写文章时 React 正处于 15.4.x 的版本 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |