避免将 props 的值复制给 state!

注意:避免将 props 的值复制给 state!这是一个常见的错误:
constructor(props) {super(props);// 不要这样做this.state = { color: props.color };}
如此做毫无必要(你可以直接使用 this.props.color),同时还产生了 bug(更新 prop 中的 color 时,并不会影响 state)。
只有在你刻意忽略 prop 更新的情况下使用。此时,应将 prop 重命名为 initialColor 或 defaultColor。必要时,你可以修改它的 key,以强制“重置”其内部 state。
直接复制 prop 到 state
最常见的误解就是 getDerivedStateFromProps 和 componentWillReceiveProps 只会在 props “改变”时才会调用。实际上只要父级重新渲染时,这两个生命周期函数就会重新调用,不管 props 有没有“变化”。所以,在这两个方法内直接复制(unconditionally)props 到 state 是不安全的。这样做会导致 state 后没有正确渲染。
重现一下这个问题。这个 EmailInput 组件复制 props 到 state:
class EmailInput extends Component {state = { email: this.props.email };render() {return <input onChange={this.handleChange} value={this.state.email} />;}handleChange = event => {this.setState({ email: event.target.value });};componentWillReceiveProps(nextProps) {// 这会覆盖所有组件内的 state 更新!// 不要这样做。this.setState({ email: nextProps.email });}}
乍看之下还可以。。state 的初始值是 props 传来的,当在 input 里输入时,修改 state。但是如果父组件重新渲染,我们输入的所有东西都会丢失!(查看这个示例),即使在重置 state 前比较 nextProps.email !== this.state.email 仍然会导致更新。
注意:示例中使用了 componentWillReceiveProps ,使用 getDerivedStateFromProps 也是一样。
getDerivedStateFromProps 的存在只有一个目的:让组件在 props 变化时更新 state。比如 props 的 offset 变化时,修改当前的滚动方向和根据 props 变化加载外部数据。
class ExampleComponent extends React.Component {state = {externalData: null,};static getDerivedStateFromProps(props, state) {// 保存 prevId 在 state 中,以便我们在 props 变化时进行对比。// 清除之前加载的数据(这样我们就不会渲染旧的内容)。if (props.id !== state.prevId) {return {externalData: null,prevId: props.id,};}// 无需更新 statereturn null;}componentDidMount() {this._loadAsyncData(this.props.id);}componentDidUpdate(prevProps, prevState) {if (this.state.externalData === null) {this._loadAsyncData(this.props.id);}}componentWillUnmount() {if (this._asyncRequest) {this._asyncRequest.cancel();}}render() {if (this.state.externalData === null) {// 渲染加载状态 ...} else {// 渲染真实 UI ...}}_loadAsyncData(id) {this._asyncRequest = loadMyAsyncData(id).then(externalData => {this._asyncRequest = null;this.setState({externalData});});}}
从 16.3 版本开始,当 props 变化时,建议使用新的 static getDerivedStateFromProps 生命周期更新 state。创建组件以及每次组件由于 props 或 state 的改变而重新渲染时都会调用该生命周期。

