六、表单
表单元素在React中自身维护一些状态,这些状态默认情况下是不受react控制的,这类状态不受react控制的表单元素称为非受控组件。
在React中,状态的修改必须通过组件的state,非受控组件有悖于这一原则,为了让表单元素的状态变更也能通过组件的state管理,React使用受控组件的技术达到这一目的。
1,受控组件
如果一个表单元素的值是由React来管理的,那么它就是一个受控组件。对于不同的表单元素,React的控制方法略有不同,常用的三类表单元素控制方式是:
①文本框
包含类型为text的input和textarea元素,它们受控的原理是:通过value属性设置表单元素的值,通过onChange时间监听值的变化,并将变化同步到React组件的state中:
class LoginForm extends Component { constructor() { this.state = { name: '', password: '' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(e) { const target = e.target; this.setState({ [target.name]: target.value }) } handleSubmit(e) { e.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 用户名: <input type="text" name="name" value={this.state.name} onChange={this.handleChange} /> </label> <label> 密码: <input type="password" name="name" value={this.state.password} onChange={this.handleChange} /> </label> <input type="submit" value="登录" /> </form> ) } } export default LoginForm;
②列表
React通过在select上定义value属性来决定哪一个option处于选中状态。
class LoginForm extends Component { constructor() { this.state = { value: '' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(e) { const target = e.target; this.setState({ value: target.value }) } handleSubmit(e) { e.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 列表: <select value={this.state.value} onChange={this.handleChange}> <option value="react">react</option> <option value="redux">redux</option> </select> </label> <input type="submit" value="登录" /> </form> ) } } export default LoginForm;
③复选框和单选框
React控制checked属性
class LoginForm extends Component { constructor() { this.state = { react: false, redux: false }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(e) { const target = e.target; this.setState({ [target.name]: target.checked }) } handleSubmit(e) { e.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> react: <input type="checkbox" name="react" value="react" checked={this.state.react} onChange={this.handleChange} /> </label> <label> redux: <input type="checkbox" name="redux" value="redux" checked={this.state.redux} onChange={this.handleChange} /> </label> <input type="submit" value="登录" /> </form> ) } } export default LoginForm;
2,,非受控组件
表单元素的状态依然由表单元素自己管理,React提供了一个特殊的属性ref,用来引用React组件或DOM元素的实例。
class LoginForm extends Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(e) { console.log(this.input.value); e.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> title: <input type="text" ref={(input) => this.input = input} /> </label> <input type="submit" value="登录" /> </form> ) } } export default LoginForm;
ref的值是一个函数,接收当前元素作为参数,上述例子中input参数指向的是当前元素,把input赋值给this.input,进而可以在组件的其他地方通过this.input获取这个元素。
使用非受控组件时,常常需要为相应的表单元素设置默认值,但无法通过value设置,因为React无法控制表单元素的value属性,一旦在非受控组件中定义了value属性的值,就很难保证后续表单元素的值的正确性。这种情况下,我们可以使用defaultValue属性指定默认值:
render() { return ( <form onSubmit={this.handleSubmit}> <label> title: <input defaultValue="something" type="text" ref={(input) => this.input = input} /> </label> <input type="submit" value="登录" /> </form> ) }
select元素和textarea元素也支持通过defaultValue设置默认值,checkbox和radio支持通过defaultChecked设置默认值。
非受控组件破坏了React对组件管理的一致性,往往容易出现不容易排查的问题,非特殊情况下,不使用。