React 受控和非受控组件
无论你做什么,都要相信自己可以做到,因为你的潜力是无限的。
把父组件的状态变成属性传递给子组件,子组件接受这个属性,听命于父组件。这个子组件就是叫做受控组件。在受控与非受控组件有两种理解方案,第一:狭义上的受控与非受控,就是我们在表单中的受控与非受控组件。第二:广义上的受控与非受控组件,就是 React 组件的数据渲染是通过父组件传递过来的属性控制的,能完全控制得住的就是受控组件,不能完全控制住的就是非受控组件。
1. 表单中非受控组件
React 要编写一个表单非受控组件,可以使用 ref 来从 DOM 节点中获取表单数据(访问节点,通过节点访问值),与状态没有任何关系,这种就是非受控组件。 下面代码使用非受控组件接受一个表单的值:
import React, { Component } from 'react'
export default class App extends Component {
myusername = React.createRef()
render() {
return (
<div>
<h4>登录页</h4>
{/* 这里设置 value 后导致 input 不能输入了。写成 value 形式本身就是一种受控方式。被字符串 “hubert” 控制住了。*/}
<input type={"text"} ref={this.myusername} value="hubert"></input>
{/* 非受控需要通过 defaultValue 设置默认值,只是第一次设置值 */}
<input type={"text"} ref={this.myusername} defaultValue="hubert"></input>
<button onClick={()=>{
console.log(this.myusername.current.value)
}}>登录</button>
<button onClick={()=>{
this.myusername.current.value = ""
}}>重置</button>
</div>
)
}
}
因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。
默认值问题:在React 渲染生命周期时,表单元素上的 value 将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新。在这种情况下,你可以指定一个 defaultvalue 属性,而不是 value。
2. 表单中受控组件
现在如果有一个子组件需要拿到表单中的 value 值,该怎么办?这里在输入框值变更的时候,是不会同步、分享到子组件的,因为 render 函数是不会重新执行的。
在 HTML 中,表单元素(如 <input>
和 <select>
)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState() 来更新。我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做"受控组件”。
{/* 在输入框值变更的时候,是不会同步、分享到子组件的 */}
<Child value={this.myusername.current.value}></Child>
// 设置成受控组件后后
<Child value={this.state.username}></Child>
import React, { Component } from 'react'
export default class App extends Component {
state = {
username: "hubert"
}
render() {
return (
<div>
<h4>登录页</h4>
{/* 这里设置 value 后导致 input 不能输入了。写成 value 形式本身就是一种受控方式。被字符串 “hubert” 控制住了。*/}
{/* 非受控需要通过 defaultValue 设置默认值,只是第一次设置值 */}
<input type={"text"} ref={this.myusername} value={this.state.username} onChange={(evt)=>{
this.setState({
username: evt.target.value
})
}}></input>
<button onClick={()=>{
console.log(this.state.username)
}}>登录</button>
<button onClick={()=>{
this.setState({
username:""
})
}}>重置</button>
{/* 在输入框值变更的时候,是不会同步、分享到子组件的 */}
{/* <Child value={this.myusername.current.value}></Child> */}
</div>
)
}
}
// 在 react 中 onInput 和 onChange 事件的行为一样的。原生 JS 则不然。