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事件并设置状态。状态值作为传递给Person组件。

Form.js

import React from 'react';
import Person './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,31);">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) {
    return (
      <div>
        <input type="text" onChange={ this.handleChange } />
        <Person name={ this.state.name } />
      </div>
    );
  }
// ...

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom