[React 基础系列] 受控表单 vs 不受控表单
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[React 基础系列] 受控表单 vs 不受控表单相关的知识,希望对你有一定的参考价值。
之前的学习部分带了一些表单内容的使用——之前的案例基本上是用的都是 input,接下来就学习一下受控表单和不受控表单之间的区别,并且通过非受控表单了解一下非受控组件的知识点。
受控表单指的是状态由 React 内部进行管理的表单,自然,不受控表单值得就是状态不由 React 内部进行管理的表单。
学习案例下载资源在:受控表单 vs 不受控表单-案例
前复习过的内容:
受控表单
受控表单指的是所有的表单状态由 state 进行管理的表单,通过 setState 触发状态更新,如下面的这个受控组件:
class ControlledForm extends Component {
constructor() {
super();
this.state = {
name: '',
phone: '',
};
this.onSubmit = this.onSubmit.bind(this);
}
updateInputHandler = (e, label) => {
this.setState((prevState) => {
return { ...prevState, [label]: e.target.value };
});
};
onSubmit = (e) => {
for (const [key, val] of Object.entries(this.state)) {
console.log(key, val);
}
// e.preventDefault(); 放置页面的重定向
e.preventDefault();
};
render() {
return (
<form onSubmit={this.onSubmit}>
<div>
<label>name: </label>
<input
type="text"
value={this.state.name}
onChange={(e) => this.updateInputHandler(e, 'name')}
/>
</div>
<div>
<label>phone: </label>
<input
type="text"
value={this.state.phone}
onChange={(e) => this.updateInputHandler(e, 'phone')}
/>
</div>
<div>
<input type="submit" value="submit" />
</div>
</form>
);
}
}
实现效果为:
这里的事件处理通过接收另外一个参数,可以做到简化状态更新的方法。这是利用了 ES6 中的计算属性名的特殊特性去实现的。通过使用 obj[variable]
这种语法可以通过传入参数(variable),做到动态更新参数(variable)的值。
更懂 ES6 的新特性可以参考: 都 2021 年了还不会连 ES6/ES2015 更新了什么都不知道吧 这篇笔记。
另外,注意到这里的 setState 的写法与之前的案例不一样,这是因为 React 更新状态并不能保证是同步地情况。为了节省效率,它内部会判断哪些 setState 是可以合并,放在一个 batch job 里面去更新。所以直接在 setState 调用 this.state
去传值是一个非常危险的行为。而通过 this.setState(prevState => {//...})
回调函数,能够保证在 setState 内获取的状态,是更新时的状态。
按照上面的图示所说,在 setState2 中调用的this.state
,极大可能会受到 setState4 的影响,从而造成更新的状态出错。这种合并更新的情况在大型应用中经常会发生,所以必须要注意这点。
确实受控组件写起来会有点麻烦,尤其是之后如果需要加其他的类型检查,那么 updateInputHandler 里面的内容会变得非常的多,这也是下一步会实现优化/封装的内容题材。
refs
refs 其实是一种可以在 React 中更直接访问 DOM 节点,或在 render 方法中创建 React 元素的方法。它的出现是针对于在组件中可能需要在非常规操作的 React 时所实用的方法。
回顾一下,常规方法即组件内通过 state 管理,父子组件通过 props 传递信息。自然,非常规方法就是不通过 state 也不通过 props 去对组件 和/或 元素进行操作。
React 官方其实是不太推荐使用 refs 去进行 DOM 的操作,因为这会放弃 Virtual DOM 带来的好处——即通过对比虚拟 DOM 树和真实 DOM 树,只重新渲染更新过的 DOM 节点,这样可以省去大量无意义的重复渲染。
React 团队列举出了 3 种使用 refs 的情景:
- 管理焦点,文本选择或媒体播放。
- 触发强制动画。
- 集成第三方 DOM 库。
一般来说,在常规的业务情况下都不太需要用到 refs……至少我到现在没用过。现在创建和使用 refs 的方法也很简单:
class MyComponent extends React.Component {
constructor(props) {
super(props);
// 通过 createRef 创建ref
this.myRef = React.createRef();
}
render() {
// 通过 ref={this.{创建的ref实例}} 就能够关联ref
return <div className="exposeRef" ref={this.myRef} />;
}
}
这样,类名为 exposeRef
的 div 元素就可以通过操作 this.myRef 的方法去修改。毕竟,refs 也可以通过 props 传递给其他的组件,这样就能够在其他的组件——通常情况下是 MyComponent 的子组件——中直接操作 exposeRef
。
不受控表单
不受控表单的内容即然无法由 state 控制,那么取值就无法通过 state 去获取了。这种情况下,也只能交给 refs 去处理了,如:
class Form extends Component {
constructor() {
super();
this.name = React.createRef();
this.phone = React.createRef();
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
console.log('submit form');
console.log(this.name.current.value, this.phone.current.value);
// e.preventDefault(); 放置页面的重定向
e.preventDefault();
}
render() {
return (
<form onSubmit={this.onSubmit}>
<div>
<input type="text" ref={this.name} />
</div>
<div>
<input type="text" ref={this.phone} />
</div>
<div>
<input type="submit" value="submit" />
</div>
</form>
);
}
}
使用效果为:
可以看到第一行的 submit form
,以及第二行也成功输出了两个 input 输入框中的值。
如果只需要表单的值,并不需要对表单的内容进行验证,使用不受控表单会方便一些,通过对比同样的代码也能看到,非受控组件的代码量少一些,实现也更简单一些。
相对而言,这种业务的应用范围也相对较少。
注意,还记得 状态 & 状态更新 & 生命周期方法 中提到的报错信息吗:
其中将 input 中的 value 改成 defaultValue 这个解决方案,主要针对的就是非受控组件而进行的提议。在非受控组件中,可以通过添加 defaultValue 这个属性去为其设置默认值。
不受控组件
不受控表单是一种不受控组件,但是不受控组件的应用范围更广一些。
相较于受控组间,不受控组件的使用情况比较少,大多数情况下都会使用到第三方库去对 div 进行管理,如使用一些可视化处理的库,这种情况下也需要搭配 componentWillUnmount 方法去清理由第三方库管理的元素。
例如说 How to properly use componentWillUnmount() in ReactJs 中被采纳的答案就使用了一个案例:
import React from 'react';
import { Component } from 'react';
import ReactDom from 'react-dom';
import d3Chart from './d3charts';
export default class Chart extends Component {
static propTypes = {
data: React.PropTypes.array,
domain: React.PropTypes.object,
};
constructor(props) {
super(props);
}
componentDidMount() {
let el = ReactDom.findDOMNode(this);
d3Chart.create(
el,
{
width: '100%',
height: '300px',
},
this.getChartState()
);
}
componentDidUpdate() {
let el = ReactDom.findDOMNode(this);
d3Chart.update(el, this.getChartState());
}
getChartState() {
return {
data: this.props.data,
domain: this.props.domain,
};
}
componentWillUnmount() {
let el = ReactDom.findDOMNode(this);
d3Chart.destroy(el);
}
render() {
return <div className="Chart"></div>;
}
}
这个案例中的 Chart 组件就不包含任何的状态,对图像进行的任何操作,都是由 d3Chart 本身去进行的状态管理。
另外,findDOMNode 这个函数在严格模式下,也就是 <React.StrictMode><其他组件 /></React.StrictMode>
下已经被废弃使用,并且官方建议使用 refs 代替 findDOMNode。
在使用第三方组件的时候一定要记得,如果在 componentDidMount 中有通过绑定 ref/findDOMNode 去创建新对象的情况,那么在 componentWillUnmount 一定要注意去销毁创建的对象,以防内存泄露的情况。
总结
这里主要就表单的内容进行了一定程度的深入了解,并且就非受控表单学习了 refs 的使用,以及在 React 中集成第三方库管理非受控组件需要注意的要点。
总结知识点如下:
-
受控表单通过 state 去进行状态管理
受控表单中的知识点包括:
-
ES6 的计算属性
通过计算属性可以快速更新对象给定参数的值
-
正确的使用 setState
这里指代的内容,失去去更新有多个键值对的状态。
为了性能的提升,React 存在压缩 setState,然后使用 batch update 去更新多个 setState,从而避免频繁的 UI 更新。这个时候,就要采取另外的写法去更新 etState,而不能通过直接调用 this.state 的方法去取值
-
-
非受控表单,让页面自己去处理值更新的问题
通过 refs 去更新和获取值
-
非受控组件,包含非受控表单,应用范围更广
采用了 StackOverflow 的一个案例,去学习非受控组件的应用
以上是关于[React 基础系列] 受控表单 vs 不受控表单的主要内容,如果未能解决你的问题,请参考以下文章