随着react v16.3版本的发布,
最大的变动莫过于生命周期去掉了以下三个
componentWillMount
componentWillReceiveProps
componentWillUpdate
同时为了弥补失去上面三个周期的不足又加了两个
static getDerivedStateFromProps
getSnapshotBeforeUpdate
如下图:
新的静态生命周期方法 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 调用一个命令式实例方法。
之前的时候
当我尝试把加了 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...