react中componentWillReceiveProps()在props不改变的时候也可能被调用

最近在写react时,需要在props被改变时更新一些东西,所以使用了componentWillReceiveProps方法,但是却发现该方法总是在各种没有改变props的情况下被调用,觉得很奇怪,遂询问我导师(我导师超厉害的!),我导师说这个方法确实有可能在props不改变的情况下被调用,所以需要在方法里手动判断一下this.props和nextProps是否相同,不相同了才执行我的更新方法。于是我抽空阅读了一下官方的文档https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/component_will_receive_props.html,研究了一下这个方法到底是怎么回事。下面先看一下componentDidMount()方法与componentWillReceiveProps()方法的对比。

componentDidMount() 在生命周期中只会被调用一次:

  • 在最开始的render后被调用(在所有的子元素都已经被render之后,并且所有的子元素都已经被调用它们的componentDidMounts 之后)
  • 组件在被卸载后的render(这确实是一个新的生命周期)

componentWillReceiveProps() 在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props。

在react的update life cycle中,不得不提到 componentWillReceiveProps方法。 该方法在props被传递给组件实例时被调用。下面详细地介绍了一下该方法。

该方法当props发生变化时执行,初始化render时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用

componentWillReceiveProps: function(nextProps) {
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}

传递 props

该方法最典型的用法是:当新的props 被传递给组件时。例如,我们有一个Form组件和一个Person组件。 Form组件有一个 <input />,允许用户通过在其中输入来更改名称。 输入绑定到 onChange 事件并设置状态。状态值作为props 传递给Person组件。

Form.js

import React from 'react';
import Person from './Person';

export default class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state        = { name: '' } ;
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.setState({ name: event.currentTarget.value });
  }

  render() {
    return (
      <div>
        <input type="text" onChange={ this.handleChange } />
        <Person name={ this.state.name } />
      </div>
    );
  }
}

当用户在输入框输入时,会使得在Person组件上调用 componentWillReceiveProps(nextProps) 方法传入新的prop值。我们可以通过调用 this.props 来获得旧的props,并且新值是传递给该方法的 nextProps 参数。

更新 State

那么为什么我们需要 componentWillReceiveProps? 因为在这里我们能抽取出新的props并且更新内部状态,比如我们可能有一些状态,这些状态是props的计算结果,那么我们就可以在这个方法里写逻辑,使用this.setState()来存储结果。 

componentWillReceiveProps方法里使用this.setState(),能够在调用render()之前更新状态,来对prop转换做出反应。 旧的props可以通过this.props访问。 在这个函数中调用this.setState()不会触发额外的渲染。

-- https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops

Props may not change

注意: componentWillReceiveProps()方法的调用,并不意味着props的值被改变。

为什么呢?数据可能在初始渲染和后续两次更新之间发生变化,而React无法知道数据有没有变化。 因此,React需要调用componentWillReceiveProps,因为组件需要被知会新的props(即使新props恰好与旧props相同)。

-- See (A => B) !=> (B => A)

假设我们有个prop,称为 data ,该prop是一个数组,如下所示:

// psuedo code
this.setState({ data: [1, 2, 3] });

<MyComponent data={ this.state.data } />

如果在程序中对该data数组进行了 push() 操作,使得data数组的内容发生了变化,但是此时数组本身(引用)并没有改变,那么react便无法判断是否该数组内部内容发生了改变,因此为了避免很多问题,也为了避免要做花费很多的深层比较,react会调用componentWillReceiveProps()方法。

因此 componentWillReceiveProps() 方法允许我们去检查、判断是否进来的新props发生了变更,让我们自己去做决定。我们只需要知道:当该方法被调用时,并不意味着props肯定会不同。(Jim Sproch 的 (A => B) !=> (B => A) 讲得十分透彻,值得一读。)

你也许认为React应该使用更聪明的检查机制来判断props是否相等,但是这样会存在几个问题:

  • 当旧的props和新的props实际上是相同的物理对象时(只更改了对象的内部值),由于引用是triple-equals-equal,所以进行相等性的检查并不能告诉我们该值是否已更改,唯一可能的解决方案是创建数据的深层拷贝,然后再进行深入比较。但对于大型数据结构(特别是具有周期的数据结构)而言,所花费的代价可能过于昂贵。
  • props对象可能会包含 对函数的引用,这些函数可能会捕获闭包中的变量。 而React无法窥视这些闭包,因此React无法拷贝它们or验证相等性。
  • props对象可能包含对父对象渲染过程中重新实例化的对象的引用(即不是triple-equals-equal),但在概念上相等(即相同的键和相同的值)。深度比较(代价昂贵)可以检测到这一点,除了函数,因为没有可靠的方法来比较两个函数以判断它们是否在语义上等价。

Skipping this method

与 Mounting 阶段的其他方法不同,并不是所有的更新阶段的方法都会每次被调用。 例如,如果update仅仅是因为一次状态更改而被触发,那么componentWillReceiveProps()将会被跳过。 回到我们上面的Form.js例子:

 // ...
 handleChange(event) {
    this.setState({ name: event.currentTarget.value });
  }

  render() {
    return (
      <div>
        <input type="text" onChange={ this.handleChange } />
        <Person name={ this.state.name } />
      </div>
    );
  }
// ...

当用户在输入框中输入时,将会触发一个 setState() 方法。这将在Form组件和Person组件中触发Update phase。对于Form组件来说,没有收到新的props,所以componentWillReceiveProps()将会被跳过。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值