原文地址: (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
会被调用多少次
在这种情况下,答案是2
。React
调用componentWillReceiveProps
两次(每次更新记录一次)。这两次,drinks
都被打印出来(componentWillReceiveProps
被调用了),但是props
没有发生改变。
要理解原因,我们需要考虑发生了什么。如果代码执行了这样的改动,那么数据可能在初始呈现和随后的两次更新之间发生了更改
React
无法知道数据有没有发生变化。因此,React
需要调用componentWillReceiveProps
,因为需要通知组件有新的props
(即使新的props
碰巧与旧props
相同)。
你可能认为React
可以使用更有效的检查来判断props
是否全等,但是这个想法存在一些问题:
- 旧的
mydata
和新的mydata
实际上是相同的实体对象(只改变了对象的内部值)。由于引用是triple-equals-equal(===),所以进行相等检查(==)不会告诉我们值是否发生了更改。唯一可能的解决方案是创建数据的深度副本,然后进行深度比较——但是对于大型数据结构(特别是具有周期的数据结构),这可能需要的代价特别高。 mydata
对象可能包含对函数的引用,这些函数在闭包中捕获了变量。 React无法窥视这些闭包,因此React无法复制它们或者验证它们的相等性。mydata
对象可能包含在父组件渲染期间重新实例化的对象的引用(即,不是triple-equals-equal(===))但在概念上相等(即,相同的键和相同的值)。深度比较(代价昂贵)可以检测到这一点,但是函数比较又出现了问题,因为没有可靠的方法来比较两个函数以确定它们在语义上是否等价。
由于语言的限制,我们有时不可能实现有意义的相等语义。在这种情况下,React
将调用componentWillReceiveProps
(即使这些props
可能没有更改),这样组件就有机会检查新的props
并采取相应的行动。
因此,componentWillReceiveProps
被调用不能认为props
已经更改。如果你想要一个操作(例如网络请求)仅在props
更改时发生,componentWillReceiveProps
代码需要检查props
是否实际更改。