之前写 AS_firedog 项目的时候,遇到一个问题,就是怎么获取到 <input />的值,最后虽然在网上找了份代码解决了,但是还是很懵懂。
在《深入 React 技术栈》中,我就领略到了 React 的表单组件和原生组件的不同,React 中很多很多组件都需要自己封装后才能实现原生的效果,而这带来的,是 React 可以对表单的状态进行控制,另外,在状态里已经可以拿到表单的值了,这一步在 html 中我们也是需要手动去处理的。
交互属性 表单组件支持几个受用户影响的属性:
value,用于<input>、<textarea>组件,获取值。
checked,用于类型为checkbox或者radio的<input>组件
selected,用于<option>组件。
当上面的三个属性发生变化时,表单组件可以通过onChange回调函数来监听组件变化。
受控组件 React 受控组件更新 state 的流程
可以通过在初始 state 中设置表单的默认值。
每单表单的值发生变化时,调用onChange事件处理器。
事件处理器通过合成事件对象e(event) 来拿到改变后的状态,并更新应用的state
setState触发 View 的重新渲染,完成表单组件值的更新。
值得注意的是,React 中我们不能想 HTML 一样把默认值设置在value中,这样子组件的值将永远不会改变。
虽然 React 表单组件比 HTML 元素的组件要复杂,每次状态改变都会执行上面这几步,可是这样让我们的表单的状态更可靠,更可控,因为这意味着我们在渲染之前,对表单的值进行清洗和校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 state = { value : 'Hello!' }handleChange (event ) { this .setState ({value : event.target .value }); }render ( ) { return ( <input type ="text" value ={this.state.value} onChange ={this.handleChange} /> ); }
下面我们可以用受控组件来截取用户输入的前 140 个字符,并转换为大写:
1 2 3 4 5 handleChange (e ){ this .setState ({ value : e.target .value .substring (0 ,140 ).toUpperCase (), }); }
受控组件不维持一个自己的内部状态;它单纯的基于 props 渲染。
不受控组件 有受控组件,自然也就有不受控组件了。受控组件通过初始 state 来设定value的默认值,靠onChange方法来监听,用setState更新组件。而不受控组件压根就不设置value属性,它自己管理自己的状态。可以通过onChange方法监听变化。
那问题来了,如果我想写不受控组件,又想设置默认值呢?
答案是可以的,可以使用 defaultValue 属性。
1 2 3 render ( ) { return <input type ="text" defaultValue ="Hello!" /> ; }
在 React 中,非受控组件是一种反模式,它的值不受组件自身的 state 或 props 控制。通常,需要通过为其添加 ref prop 来访问渲染后的底层 DOM 元素。defaultValue 和 defaultChecked props 只能在内部渲染时被使用。通过 defaultValue 或者 defaultChecked 来设置表单的默认值,它仅会被渲染一次,在后续的渲染时并不起作用。下面对比以下两个示例。
textarea 使用 React 的<textarea>的值不像 HTML 一样写成子节点。
1 <textarea name="description" value="This is a description." />
当然也不是不能那样写,如果 非要 使用子节点,效果和使用 defaultValue 一样。
栗子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class EssayForm extends React.Component { constructor (props ) { super (props); this .state = { value : 'Please write an essay about your favorite DOM element.' }; this .handleChange = this .handleChange .bind (this ); this .handleSubmit = this .handleSubmit .bind (this ); } handleChange (event ) { this .setState ({value : event.target .value }); } handleSubmit (event ) { alert ('An essay was submitted: ' + this .state .value ); event.preventDefault (); } render ( ) { return ( <form onSubmit ={this.handleSubmit} > <label > Name: <textarea value ={this.state.value} onChange ={this.handleChange} /> </label > <input type ="submit" value ="Submit" /> </form > ); } }
select 使用 1 2 3 4 5 6 <select> <option value ="grapefruit" > Grapefruit</option > <option value ="lime" > Lime</option > <option selected value ="coconut" > Coconut</option > <option value ="mango" > Mango</option > </select>
如果是不受控组件,则使用 defaultValue。
注意:给 value 属性传递一个数组,可以选中多个选项:<select multiple={true} value={['B', 'C']}>。
栗子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class FlavorForm extends React.Component { constructor (props ) { super (props); this .state = {value : 'coconut' }; this .handleChange = this .handleChange .bind (this ); this .handleSubmit = this .handleSubmit .bind (this ); } handleChange (event ) { this .setState ({value : event.target .value }); } handleSubmit (event ) { alert ('Your favorite flavor is: ' + this .state .value ); event.preventDefault (); } render ( ) { return ( <form onSubmit ={this.handleSubmit} > <label > Pick your favorite La Croix flavor: <select value ={this.state.value} onChange ={this.handleChange} > <option value ="grapefruit" > Grapefruit</option > <option value ="lime" > Lime</option > <option value ="coconut" > Coconut</option > <option value ="mango" > Mango</option > </select > </label > <input type ="submit" value ="Submit" /> </form > ); } }
总之,<input type="text">, <textarea>, 和 <select> 都十分类似 - 他们都通过传入一个value属性来实现对组件的控制。
多个输入的解决方法 当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么。
栗子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Reservation extends React.Component { constructor (props ) { super (props); this .state = { isGoing : true , numberOfGuests : 2 }; this .handleInputChange = this .handleInputChange .bind (this ); } handleInputChange (event ) { const target = event.target ; const value = target.type === 'checkbox' ? target.checked : target.value ; const name = target.name ; this .setState ({ [name]: value }); } render ( ) { return ( <form > <label > Is going: <input name ="isGoing" type ="checkbox" checked ={this.state.isGoing} onChange ={this.handleInputChange} /> </label > <br /> <label > Number of guests: <input name ="numberOfGuests" type ="number" value ={this.state.numberOfGuests} onChange ={this.handleInputChange} /> </label > </form > ); } }
本文代码来源 discountry ; 2017 年 07 月 28 日 00:18:30