今天看啥  ›  专栏  ›  yanziNote

react UNSAFE生命周期函数替换

yanziNote  · 掘金  ·  · 2019-12-12 13:12
阅读 9

react UNSAFE生命周期函数替换

react 生命周期函数变更

react v16.3 版本的发布,生命周期函数的变动去掉了以下三个

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

同时为了弥补失去上面三个周期的不足又加了两个

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

过度方案是在之前项目中使用去掉的函数添加前缀 UNSAFE_, 官网提示可以继续使用至 React 17。但在项目中依然会有一些waring ,建议修改。以下是项目中一些替换策略,涉及也是比较全面,且有我深入浅出一一阐述。

UNSAFE_componentWillMount 的替换

componentWillMount 函数中的场景有一下种:

  • class component中state 初始化数据状态的设置
    • 官网建议使用 constructor() 来初始化 state。
  • 异步拉取数据
    • 可以放在componentDidMount 函数中 引用官网原句在此方法中引入任何副作用或订阅。如遇此种情况,请改用 componentDidMount()
  • 定时器
    • 可以在 componentDidMount 函数中
    • 另一种方式是把定时器的抽离新的子组件(涉及业务逻辑的都可以放在子组件中)

UNSAFE_componentWillReceiveProps 的替换

componentWillReceiveProps 函数在初始的props不会渲染。会在组件接受到新的 props 时调用。一般用于父组件更新状态时子组件的重新渲染。替换的场景如下:

  • 对比 this.prop和nextProps
  • 根据 nextProps更新state的操作 (见例子2)
    • 不影响dom界面的显示
    • 影响dom界面的显示
  • 根据 nextProps更新state,并且有调用this.props的操作(见例子1)

替换策略:

  • 如果您需要 执行副作用 (side effect)(例如,数据获取或动画)以响应 属性 (props) 的更改,使用 componentDidUpdate 生命周期方法

  • 使用 componentWillReceiveProps, 在属性 (props) 改变 时重新计算一些数据,请使用 memoization 辅助工具-官网有例子

  • 使用 componentWillReceiveProps , 在属性 (props) 改变时 “重置” 一些 state (状态),考虑使用一个 完全控制组件 或 一个 带 key 的完全不受控 组件

  • 可以使用 static getDerivedStateFromProps + componentDidUpdate vs getSnapshotBeforeUpdate + componentDidUpdate

以上3种情况是官网推荐, 也是比较简单的方式。static getDerivedStateFromProps 函数是官网也不推荐使用的函数,所以命名就比较长, 项目中对对此函数的替换在不拆分组件, 没有使用hook的情况下使用第3种策略进行的改写。

static getDerivedStateFromProps(props, state)在调用 render 方法之前被调用,包括初始装载(mount)和后续更新时。 它应该返回一个更新 state (状态) 的对象,或者返回 null 以不更新任何 state (状态)。

getSnapshotBeforeUpdate(prevProps, prevState) 在最近一次的渲染输出被提交之前调用, 它返回的值将作为第三个 snapshot 参数传递给 componentDidUpdate() 。 否则这个参数将是 undefined componentDidUpdate(prevProps, prevState, snapshot)此方法可以调用 setState()来操作 DOM 。但请注意,必须包含在条件语句中 像上面的例子一样,否则你会导致无限循环。 这也会导致额外的重新渲染, 虽然对用户不可见,但是会影响组件的性能。

例子1更改前

 UNSAFE_componentWillReceiveProps(nextProps) {
    const { models = [], itemId, shopId } = nextProps.sellerData;
    const { itemId: currentItemId } = this.props.sellerData;
    if (itemId !== currentItemId && models.length && models.length > 0) {
      this.setState(
        {
          matchShop: {},  // 初始化state
        },
         // callback 中调用 this.props 中父组件的方法
        () => {
          this.init(models);
          this.props.onGetMatchingTable({
            modelid: models[0].model_id,
            itemId,
            shopId
          });
        }
      );
    }
  }

复制代码

例子1更改后

// getDerivedStateFromProps 接收最新的 Props 值 nextProps、上一个 state 值 prevState 两个参数,返回返回一个对象来更新 state,或者返回 null 表示不需要更新 state。

 static getDerivedStateFromProps(nextProps, prevState) {
    const { models = [], itemId, shopId } = nextProps.sellerData;
    const { itemId: currentItemId } =
      (prevState && prevState.preSellerData) || '';

    if (
      currentItemId !== '' &&
      itemId !== currentItemId &&
      models.length &&
      models.length > 0
    ) {
      return {
        matchShop: {},
      };
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState) {
    const { itemId: currentItemId } = prevProps.sellerData;
    const { models = [], itemId, shopId } = this.props.sellerData;
    if (itemId !== currentItemId && models.length && models.length > 0) {
      // console.log('prevState---this.props', this.props, prevState, prevProps);
      this.init(models);
      this.props.onGetMatchingTable({
        modelid: models[0].model_id,
        itemId,
        shopId
      });
    }
  }


复制代码

例子2 state状态中iptUrl 是在子组件中,并且都多处可以变更,父组件传值props 用户可以输入url,这种情况下使用 getDerivedStateFromProps静态方法就会触发页面更改进入死循环。使用getSnapshotBeforeUpdate + componentDidUpdate进行替换修改。

 // 更改前注释状态
 
  // UNSAFE_componentWillReceiveProps(nextProps) {
  //   if (nextProps.url !== this.state.iptUrl) {
  //     this.setState({
  //       iptUrl: nextProps.url,
  //       errMsg: ''
  //     });
  //   }
  // }
  
  
  // 更改后 
  getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.url !== this.props.url) {
      return this.props.url;
    }
    return null;
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      this.setState({ iptUrl: this.props.url, errMsg: '' });
    }
  }
复制代码

使用 Effect Hook

在React 16.8 版本中可以使用 Effect Hook, useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。在hook 中没有生命周期的概念,更改数据的操作都可以称为副作用。Effect Hook 可以让你在函数组件中执行副作用操作。

参考

react




原文地址:访问原文地址
快照地址: 访问文章快照