之前写 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