首发于 语雀文档@blueju
前言
上周,我去组员那检查工作情况的时候,他说有困难,有一个页面不知道怎么写?
我瞅了瞅,大概需求是:填完表单后,下面的表格就会显示一些信息,还需要能支持搜索等操作。其实如果这些都写在同一个组件,so easy,但是它把显示表格单独写成了一个组件,大概是这样:
复现
当时帮他处理了,现在我模拟一个类似的场景来复现一下,使用了 antd 组件库。
https://codesandbox.io/s/shi-yong-getderivedstatefromprops-shi-xian-ban-shou-kong-zu-jian-cuqyz9
组件B接收组件A传入的数据并进行展示,但是期望组件B内又能对传入的数据进行查询。
分析
这是个比较典型的半受控组件,需要组件B在接收组件A传入数据进行展示的同时,又能完全控制一些内部的状态。
组件B接收组件A传入的数据并进行展示,如果仅仅如此,很好说,antd Table 组件的 dataSource 的值直接使用 props 内的值即可。
但是我们期望组件B内又能对传入的数据进行查询,那势必 Table 组件的值需要由另外的状态进行控制,state ① 它初始化时接收 props 内的值、② props 更新时接收 props 内的值、③ 内部查询时接收对 props 进行筛选后的值。
这样子我们就能做到有 props 时用 props,内部数据需要变化时更新 state 而不需要触发外部方法更新 props,将 state 作为中间状态的组件半受控状态。
初始化时 state 接收 props 内的值
class PersonalInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
id: "",
dataSource: this.props.dataSource,
prevProps: this.props,
};
}
...
}
我们暂且只关注 state 中的 id 和 dataSource,在组件实例化时这么设置 dataSource 这么有用吗?其实没啥用,实例化是一次性的,在后续 props 更新时并不会自动更新至 state 中。
这么写唯一的用处可能就是,一眼让我们知道了 dataSource 它对应的是 props 中的 dataSource,哦是个对象数组。
props 更新时 state 接收 props 内的值
/**
* 参考:
* 1、https://zh-hans.reactjs.org/docs/react-component.html#static-getderivedstatefromprops
* 2、https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
* @param {*} nextProps
* @param {*} prevState
*/
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps !== prevState.prevProps) {
return {
dataSource: nextProps.dataSource,
prevProps: nextProps,
};
}
return null;
}
这里就需要使用到 getDerivedStateFromProps 这个生命周期了。
对于半受控组件,state 作为一个中间状态,一个很重要的点就是:props 变化时,state 要更新,props 没变化时,state 也不需要更新,所以我们需要将 nextProps 和 prevProps 比较一下,确认是否需要更新 state。
但 getDerivedStateFromProps 它是个静态方法,无法获取实例的 this,因此我们无法直接比较 nextProps 和 prevProps,因此只能通过 state 将其暂存起来。
内部查询时 state 接收对 props 进行筛选后的值
handleChange = (e) => {
let dataSource;
if (e.target.value.trim().length) {
dataSource = this.props.dataSource.filter(({ id }) => {
return id.includes(e.target.value);
});
} else {
dataSource = this.props.dataSource;
}
this.setState({
id: e.target.value,
dataSource,
});
};
这一块理解起来并不难,只是要注意我们是对 this.props.dataSource 进行过滤操作,而不是 this.state.dataSource。
拓展
其实这篇文章之以及 CodeSandBox 里的 demo 写了挺久,写的过程中一直在想还有没有别的办法、这么写示例代码对不对、这么描述准不准确。
不用 getDerivedStateFromProps 实现行不行
https://codesandbox.io/s/bu-shi-yong-getderivedstatefromprops-shi-xian-ban-shou-kong-zu-jian-rlgo96
用函数组件该如何实现
https://codesandbox.io/s/useeffect-ban-shou-kong-zu-jian-n6b4ur
最后
整体下来,我觉得其实还是纯组件那一套:对 props 进行管理,只是换一种形式,根据业务逻辑将 props 转化为 state。