React 的 getDerivedStateFromProps 生命周期 究竟有啥用 ?

随着react v16.3版本的发布,

最大的变动莫过于生命周期去掉了以下三个

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

static getDerivedStateFromProps
getSnapshotBeforeUpdate
如下图:
53884612-fa2b0800-4056-11e9-879f-050e0b33e0e9.png?ynotemdtimestamp=1551878671356

新的静态生命周期方法 getDerivedStateFromProps 是根据props来派生出新的state

前段时间在使用 getDerivedStateFromProps 时,只是机械的用它和 componentDidUpdate 来替换 componentWillReceiveProps,已达到通过修改props,来修改state的作用,比如下面这个例子:

// Before
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };
 
  componentDidMount() {
    // this.props.id === undefined
    this._loadData(this.props.id);
  }
 
  componentWillReceiveProps(nextProps) {
      //当某个props中的值发生变化时(此处是id属性)
      if (nextProps.id !== this.props.id) {
        //初始化state中的externalData值为null
        this.setState({externalData: null});
        //基于新的id属性,异步获取数据,并在完成时设置externalData的值
        this._loadData(nextProps.id);
    }
  }
 
  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
     
  _loadData(id) {
  
    if (!id) {return;}
  
    loadData(id).then(
      externalData => {
        this.setState({externalData});
      }
    );
  }
}

改写之后:

// After Wrong
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
    prevId: this.props.id
  };
 
  static getDerivedStateFromProps(nextProps, prevState) {
    // Store prevId in state so we can compare when props change.
    // Clear out previously-loaded data (so we don't render stale stuff).
    if (nextProps.id !== prevState.prevId) {
      return {
        externalData: null,
        prevId: nextProps.id,
      };
    }
 
    // No state update necessary
    return null;
  }
 
  componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadData(this.props.id);
    }
  }
 
  componentDidMount() {
    this._loadData(this.props.id);
  }
 
  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
 
  _loadData(id) {
  
    if (!id) {return;}
  
    loadData(id).then(
      externalData => {
        this.setState({externalData});
      }
    );
  }
}

getDerivedStateFromProps 存在只为了一个目的。它让组件在 props 发生改变时更新它自身的内部 state。 一个常规的准则,应该保守地使用 derived state。

看了一下:https://reactjs.org/blog/2018... 中有提到两个使用 getDerivedStateFromProps 的反模式:

无条件地将 prop 复制给 state
当 props 改变时清除 state,使 props 和 state 中的值,保持一致
这个例子比较符合第二种情况。就是为了比较id是否更新,把props中的id,存到了state 中 prevId。
并且 在 componentDidUpdate 的时候, 根据 state 中的 externalData 是否改变来判断是否需要异步加载新的数据。

这样写也没有错误,但是没有必要这样写,这样写反而增加了程序的复杂程度。

// Right
class ExampleComponent extends React.Component {
  state = {
    externalData: null,
  };
 
  componentDidUpdate(prevProps, prevState) {
    if (this.props.id && this.props.id !== prevProps.id) {
      this._loadData(this.props.id);
    }
  }
 
  componentDidMount() {
    this._loadData(this.props.id);
  }
 
  render() {
    if (this.state.externalData === null) {
      // Render loading state ...
    } else {
      // Render real UI ...
    }
  }
 
  _loadData(id) {
  
    if (!id) {return;}
  
    loadData(id).then(
      externalData => {
        this.setState({externalData});
      }
    );
  }
}

因为上面的例子属于异步的更新,所以直接使用 componentDidUpdate 来确保是基于正确的 props 来更新 state 足以。

对不受控组件,如果你在一个特殊的 prop (通常是 ID)改变时试图重置 state,你有一些选择:

推荐:重置所有内部 state,使用 key 属性
替代方案1:仅重置确定的 state 字段,监听特定属性的变化(例如: props.id)。
替代方案2:你也可以考虑使用 refs 调用一个命令式实例方法。

阅读 8.3k
1 个回答

之前的时候

当我尝试把加了 getDerivedStateFromProps 的代码合并到主分支,代码审查员没有给过我一次机会... [getDerivedStateFromProps] 他到底有什么用呢 (╯°□°)╯︵┻━┻

16.4新版的之后,getDerivedStateFromProps 的执行时间已经改变了:https://projects.wojtekmaj.pl...

你可能不需要使用派生 state:
https://zh-hans.reactjs.org/b...

现在看来,getDerivedStateFromProps 这个生命周期更适合做一些优化;比如一些极端的情况下,根据props的改变来重置全部或者一部分状态:
如果某些情况下 key 不起作用(可能是组件初始化的开销太大),一个麻烦但是可行的方案是在 getDerivedStateFromProps 观察 userID 的变化:https://codesandbox.io/s/rjyv...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏