[译](A => B) !=> (B => A)

651 阅读3分钟

原文地址: (A => B) !=> (B => A)
原文作者: Jim Sproch
译者: arzh
推荐理由: react官网中推荐的博客,文章可以帮忙深入理解componentWillReceiveProps这个属性,虽然在17版本准备废弃此生命周期,但是这篇文章对componentWillReceiveProps的理解还是值得我们学习的

componentWillReceiveProps的文档声明,当props作为重新渲染的结果发生变化时,将调用componentWillReceiveProps。有些人认为这意味着“如果componentWillReceiveProps被调用,那么props一定是已经改变”,但这个结论在逻辑上是不正确的。

指导原则是我对形式逻辑/数学的最爱之一:

A能推导出B并不意味着B能推导出A

例如:“如果我吃发霉的食物,我就会生病”并不意味着“如果我生病了,那么我一定是吃了发霉的食物”。我生病还有许多其他的原因。例如,也许流感正在办公室传播。类似地,即使props没有改变,还是有很多原因可以使componentWillReceiveProps被调用。

如果你不相信,用相同的props调用ReactDOM.render()三次,试着预测一下componentWillReceiveProps会被调用多少次

在这种情况下,答案是2React调用componentWillReceiveProps两次(每次更新记录一次)。这两次,drinks都被打印出来(componentWillReceiveProps被调用了),但是props没有发生改变。

要理解原因,我们需要考虑发生了什么。如果代码执行了这样的改动,那么数据可能在初始呈现和随后的两次更新之间发生了更改

React无法知道数据有没有发生变化。因此,React需要调用componentWillReceiveProps,因为需要通知组件有新的props(即使新的props碰巧与旧props相同)。

你可能认为React可以使用更有效的检查来判断props是否全等,但是这个想法存在一些问题:

  1. 旧的mydata和新的mydata实际上是相同的实体对象(只改变了对象的内部值)。由于引用是triple-equals-equal(===),所以进行相等检查(==)不会告诉我们值是否发生了更改。唯一可能的解决方案是创建数据的深度副本,然后进行深度比较——但是对于大型数据结构(特别是具有周期的数据结构),这可能需要的代价特别高。
  2. mydata对象可能包含对函数的引用,这些函数在闭包中捕获了变量。 React无法窥视这些闭包,因此React无法复制它们或者验证它们的相等性。
  3. mydata对象可能包含在父组件渲染期间重新实例化的对象的引用(即,不是triple-equals-equal(===))但在概念上相等(即,相同的键和相同的值)。深度比较(代价昂贵)可以检测到这一点,但是函数比较又出现了问题,因为没有可靠的方法来比较两个函数以确定它们在语义上是否等价。

由于语言的限制,我们有时不可能实现有意义的相等语义。在这种情况下,React将调用componentWillReceiveProps(即使这些props可能没有更改),这样组件就有机会检查新的props并采取相应的行动。

因此,componentWillReceiveProps被调用不能认为props已经更改。如果你想要一个操作(例如网络请求)仅在props更改时发生,componentWillReceiveProps代码需要检查props是否实际更改。