避免将 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,
      };
    }
    // 无需更新 state
    return 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 的改变而重新渲染时都会调用该生命周期。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值