网上多数文章仅仅讲述了受控组件和非受控组件的概念,并没有讲述为什么会存在这两种类型组件以及它们的应用场景。本文将对这些遗留问题进行介绍。
1、什么是受控组件和非受控组件?
受控组件
在 HTML 中,表单元素(如 <input>
、<textarea>
和<select>
)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
由于在表单元素上设置了 value
属性,因此显示的值将始终为 this.state.value
,这使得 React 的 state 成为唯一数据源。由于handlechange
在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。
对于受控组件来说,每个 state 突变都有一个相关的处理函数。这使得修改或验证用户输入变得简单。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title> 受控组件 </title>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<!-- <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> -->
<!-- <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class NameForm extends React.Component {constructor(props) {super(props)
this.state = {
name: '',
pwd: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {console.log(e)
var key = e.target.name
var value = e.target.value
this.setState({[key]: value})
}
handleSubmit(e) {alert('姓名:' + this.state.name + '密码:' + this.state.pwd)
e.preventDefault()}
render() {
return (<form onSubmit={this.handleSubmit}>
<label>
名字:<input type="text" name="name" value={this.state.name} onChange={this.handleChange} />
<br/>
密码:<input type="password" name="pwd" value={this.state.pwd} onChange={this.handleChange} />
</label>
<br/>
<input type="submit" value="提交" />
</form>
)
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
)
</script>
</body>
</html>
非受控组件
要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。
因为 非受控组件将真实数据储存在 DOM 节点中,所以再使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。
在 React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。在这种情况下, 你可以指定一个 defaultValue 属性,而不是 value。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title> 非受控组件 </title>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<!-- <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> -->
<!-- <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
class NameForm extends React.Component {constructor(props) {super(props)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(e) {e.preventDefault()
console.log(this.input)
alert(this.input.value)
}
render() {
return (<form onSubmit={this.handleSubmit}>
<label>
名字:
<input name="username" type="text" defaultValue="123456" ref={username => this.input = username} />
</label>
<input type="submit" value="提交" />
</form>
)
}
}
ReactDOM.render(
<NameForm />,
document.getElementById('root')
)
</script>
</body>
</html>
我们可以简单总结一下两种类型组件的区别:
1、受控组件表单数据由 state 管理,非受控组件表单数据由 DOM 节点管理;
2、受控组件表单数据变更具有实时性,具有双向数据绑定特性,非受控组件表单数据是一次性的,需要时读取,受控组件能帮助我们更好的控制表单元素。
2、两者适应场景
根据上述的比较,我们可以总结出它们的应用场景
参考:
https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/