受控组件与非受控组件
对于页面上元素的属性值,React 提供了两种方式来管理这些元素的值。比如常见的表单元素,React 提供了两种方式来管理表单元素的输入。受控组件和非受控组件,这两种组件在处理输入数据的方式上有着根本的区别。
受控组件
受控组件是指表单元素的输入值由 React 组件的状态(state)进行控制。在受控组件中,表单元素的值由组件的状态驱动,任何对输入值更改的操作都需要通过状态更新来实现。
受控组件的值由props或state传入,每当用户输入数据时,组件会通过事件处理函数更新状态,并将新的状态传递给表单元素。
假如组件没有绑定事件处理函数改变组件的值,用户的输入是不会起到任何效果的,这也就是受控的含义所在。
受控组件的两个要点:
- 表单元素的
value属性与组件的state状态进行绑定 - 组件内声明了事件处理
value的变化
代码示例
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg:"",
}
changeHandle = (e) => {
console.log(e.target.value);
}
render() {
return (
<div>
<input type="text" value={this.state.msg} onChange={this.changeHandle} />
</div>
)
}
}
export default App;
没有使用setState函数来更新数据所以页面不会刷新并渲染用户输入的数据。
事件处理函数中调用setState更新状态
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "",
}
changeHandle = (e) => {
console.log(e.target.value);
this.setState({
msg:e.target.value
});
}
render() {
return (
<div>
<input type="text" value={this.state.msg} onChange={this.changeHandle} />
</div>
)
}
}
export default App;
在上面这个案例中:
- msg是一个状态(
this.state.msg),用于存储输入框的值 - 表单元素的
value属性绑定了this.state.msg,使得输入框的值由状态控制 onChange事件的事件处理函数(this.changeHandle)在每次输入时更新状态,确保组件的状态与用户输入保持同步
非受控组件
非受控组件是指表单元素的输入值不由 React 组件的状态控制,而是通过直接操作 DOM 元素来获取输入值。
在非受控组件中,表单输入的值存在于 DOM 元素中,而不是 React 组件的状态。要获取当前的输入值,通常使用 React 的 ref 来访问 DOM 元素并获取其值。
代码示例
import { PureComponent,createRef } from 'react'
class App extends PureComponent {
// 创建 ref
inputRef = createRef();
handleButtonClick = () => {
// 通过 ref 获取输入框的值
const inputValue = this.inputRef.current.value;
console.log('输入框的值:', inputValue);
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.handleButtonClick}>获取输入框值</button>
</div>
)
}
}
export default App;
在上面这个案例中:
- 使用
createRef创建了一个inputRef,用于引用输入框 - 在
handleButtonClick函数中,通过this.inputRef.current.value获取当前输入框的值
受控组件与非受控组件的对比
受控组件的优点:
- 单一数据源:所有表单数据都存储在组件的状态中,易于管理和维护
- 可控性:开发者可以轻松地验证输入、清空表单等操作,便于实现复杂的表单逻辑
- 实时反馈:可以在输入过程中实现即时反馈,增强用户体验
受控组件的缺点:
- 性能开销:对于大量表单元素的复杂组件,频繁更新状态可能导致性能问题
- 繁杂的代码:需要编写额外的事件处理逻辑,可能使代码变得冗长
受控组件的适用场景:
- 需要实时反馈:当需要根据用户输入即时更新其他界面元素时,受控组件更为合适
- 表单验证:对于需要复杂验证逻辑的表单,使用受控组件可以更方便地进行状态管理
- 动态表单:当表单字段的数量或类型是动态变化时,使用受控组件可以更好地管理状态
非受控组件的优点:
- 简单实现:不需要额外的状态管理,代码相对简洁
- 适用于复杂表单:在某些情况下,不需要实时更新状态,使用非受控组件能够减少渲染次数
非受控组件的缺点:
- 难以维护:由于数据不在组件的状态中,跟踪输入变化变得困难
- 缺乏实时反馈:无法实现即时验证或反馈,用户体验可能受到影响
- 违背 React 的理念:非受控组件使用了直接操作 DOM 的方式,违背了 React 的声明式编程理念
非受控组件的适用场景:
- 简单表单:对于简单的表单,只需要提交数据而不需要实时更新状态时,非受控组件可以减少代码复杂性
- 不需要实时验证:如果不需要对用户输入进行实时验证,非受控组件可以更轻松地管理
常见的表单组件的绑定
在组件中声明表单元素input,textarea,select等时,一般都要为表单元素传入应用状态中的值,然后根据表单元素中用户的输入,对应用数据进行相应的操作和改变。常见的表单包括输入框,单选框,复选框,下拉框和多文本框,在Vue中只需要使用v-model指令,但是在React中需要自己实现双向数据的绑定。
输入框
先给input框的value绑定一个值,然后通过change事件来获取用户输入的值,并将这个值赋值给value属性绑定的值,再使用setState方法来重新渲染组件就可以实现数据的双向绑定了,最后表单收集的就是用户输入的value值。
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "",
}
changeHandle = (e) => {
console.log(e.target.value);
this.setState({
msg:e.target.value
});
}
render() {
return (
<div>
<input type="text" value={this.state.msg} onChange={this.changeHandle} />
</div>
)
}
}
export default App;
在react中
onInput事件是在用户更改值时立即触发。在 React 中习惯于使用onChange事件,onChange事件在用户更改输入框的值时立即触发,每次按键时都会触发。行为类似于浏览器的 input 事件。
在vue中
@change事件会在表单元素的值发生变化并失去焦点(通常是用户输入后按下回车键或点击其他地方)时触发。而@input事件是在每次输入时都会触发。这类似于原生的input、change事件。
单选框和复选框
radio单选框和checkbox复选框通过操作checked属性实现受(checked为true表示选中,checked为false表示未选中),value属性只起辅助作用。
单选框
单选框可以通过onChange事件获取value的值,根据value值间接控制是否选中,但是onChange只能获取value值并不能改变value值,将选中的value值赋值给表单要收集的数据变量就可以收集到选中的value值。
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
sex: "",
}
sexhandle = (e) => {
console.log(e.target.value);
this.setState({
sex: e.target.value
});
}
render() {
return (
<div>
<label>
<input type="radio" name="sex" value='男' checked={this.state.sex == '男'} onChange={this.sexhandle} />男
</label>
<label>
<input type="radio" name='sex' value='女' checked={this.state.sex == '女'} onChange={this.sexhandle} />女
</label>
</div>
)
}
}
export default App;
复选框
复选框checked属性是对数组进行操作,通过一个state状态中的数组来存储变更。也是通过value值间接控制是否选中,onChange同样只能获取value值,不能改变,在数组中收集选中的value值。
import { Component } from 'react'
class App extends Component {
state = {
hobby: [],
}
hobbyhandle = (e) => {
console.log(e.target.value, 88);
if(this.state.hobby.includes(e.target.value)){
// 已选中的再次点击取消选中,将value从数组中取消
this.state.hobby.splice(this.state.hobby.indexOf(e.target.value),1);
}else{
// 未被选中的添加到数组中
this.state.hobby.push(e.target.value)
}
console.log(this.state.hobby, 111);
this.setState(this.state);
}
render() {
return (
<div>
<label>
<input type="checkbox" value='篮球' checked={this.state.hobby.includes('篮球')} onChange={this.hobbyhandle} />篮球
</label>
<label>
<input type="checkbox" value='高尔夫' checked={this.state.hobby.includes('高尔夫')} onChange={this.hobbyhandle} />高尔夫
</label>
<label>
<input type="checkbox" value='乒乓球' checked={this.state.hobby.includes('乒乓球')} onChange={this.hobbyhandle} />乒乓球
</label>
<label>
<input type="checkbox" value='躺平摆烂' checked={this.state.hobby.includes('躺平摆烂')} onChange={this.hobbyhandle} />躺平摆烂
</label>
</div>
)
}
}
export default App;
select下拉框
关于select,可以通过循环渲染option,在之前的select中,value都是写在option上的,但是在React中,对select进行了简单的封装,需要将value放在select中就可以通过select的onChange事件获取当前选择的value。
import { Component } from 'react'
class App extends Component {
state = {
city: "",
}
cityarr = ["北京","深圳","杭州","南京","上海"]
cityhandle = (e) => {
this.state.city = e.target.value;
console.log(this.state.city, 111);
this.setState(this.state);
};
render() {
return (
<div>
<select value={this.state.city} onChange={this.cityhandle}>
{
this.cityarr.map((value, index) => {
return <option key={index}>{value}</option>
})
}
</select>
</div>
)
}
}
export default App;
日期选择器
收集的是value值,就是选择的日期
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
date: "",
}
datehandle = (e) => {
this.setState({
date: e.target.value
}, () => {
console.log(this.state.date, 111)
});
};
render() {
return (
<div>
<input type="date" value={this.state.date} onChange={this.datehandle} />
</div>
)
}
}
export default App;
文件类型
通过e.target.file来获取用户选择的文件,它是一个数组。
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
file: [],
}
filehandle = (e) => {
console.log(e.target.files);
this.setState({
file: [...this.state.file,e.target.files[0]]
}, () => {
console.log(this.state.file, 111)
});
};
render() {
return (
<div>
<input type="file" onChange={this.filehandle}/>
</div>
)
}
}
export default App;
文本框
文本框和输入框的text同理,收集的是value就是用户输入的内容。
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
text: ""
}
texthandle = (e) => {
this.setState({
text: e.target.value
}, () => {
console.log(this.state.text, 111)
});
};
render() {
return (
<div>
<textarea value={this.state.text} onChange={this.texthandle}></textarea>
</div>
)
}
}
export default App;