天,我竟不懂 getDerivedStateFromProps

首发于 语雀文档@blueju

前言

上周,我去组员那检查工作情况的时候,他说有困难,有一个页面不知道怎么写?

我瞅了瞅,大概需求是:填完表单后,下面的表格就会显示一些信息,还需要能支持搜索等操作。其实如果这些都写在同一个组件,so easy,但是它把显示表格单独写成了一个组件,大概是这样:
image.png

复现

当时帮他处理了,现在我模拟一个类似的场景来复现一下,使用了 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。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值