基于 props 更新 state

单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。

Props 的只读性

决不能修改自身的 props。所有组件都必须保护它们的 props 不被更改。

State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。state 允许组件随用户操作、网络响应或者其他变化而动态更改输出内容。

让组件在 props 变化时更新 state

在 vue 中我们会这样做,定义一个本地的 data 属性并将这个 prop 用作其初始值,然后通过 watch 监控 prop 变化,然后重复赋值给本地的 data 属性。

props: ['initialCounter'],
data () {
  return {
    counter: this.initialCounter
  }
},
watch: {
  initialCounter (newCounter) {
    if (newCounter !== this.counter) {
      this.counter = newCounter
    }
  }
}

在 react 中,从 16.3 版本开始,当 props 变化时,建议使用新的 static getDerivedStateFromProps 生命周期更新 state。创建组件以及每次组件由于 props 或 state 的改变而重新渲染时都会调用该生命周期:

class ExampleComponent extends React.Component {
  // 在构造函数中初始化 state,
  // 或者使用属性初始化器。
  state = {
    counter: this.props.initialCounter,
  };


  static getDerivedStateFromProps(props, state) {
    if (props.initialCounter !== state.counter) {
      return { counter: props.initialCounter };
    }


    // 返回 null 表示无需更新 state。
    return null;
  }


  handleClick = () => {
    // 点击之后无法修改 counter 的 bug
    this.setState({counter: this.state.counter + 1})
  }


  render() {
    return (
      <div onClick={this.handleClick}>{this.state.counter}</div>
    );
  }
}

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

请注意,不管原因是什么,都会在每次渲染前触发此方法。这与 componentWillReceiveProps 形成对比,后者仅在父组件重新渲染时触发,而不是在内部调用 setState 时。

基于 props 更新 state,旧的 componentWillReceiveProps 和新的 getDerivedStateFromProps 方法都会给组件增加明显的复杂性。这通常会导致 bug。

最常见的误解就是 getDerivedStateFromProps 和 componentWillReceiveProps 只会在 props “改变”时才会调用。实际上只要父级重新渲染时,这两个生命周期函数就会重新调用,不管 props 有没有“变化”。所以,在这两个方法内直接复制 props 到 state 是不安全的。这样做会导致 state 后没有正确渲染。

希望以上能解释清楚为什么直接复制 prop 到 state 是一个非常糟糕的想法。

虽然这个设计就有问题,但是这样的错误很常见,(我就犯过这样的错误)。任何数据,都要保证只有一个数据来源,而且避免直接复制它。

编写完全可控的组件

阻止上述问题发生的一个方法是,从组件里删除 state。然后传入在父组件中定义的处理函数进行修改。

class ExampleComponent extends React.Component {
  render() {
    return (
      <div onClick={this.props.handleClick}>
        {this.props.initialCounter}
      </div>
    );
  }
}

虽然 vue 中没有这个问题,但是建议大家不要在组件里面修改 props,任何数据,都要保证只有一个数据来源,而且避免直接复制它。都必须保护它们的 props 不被更改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值