遵守不变性
这是一个很老的问题,但它是一个长期存在的问题,如果只有错误的答案和解决方法,它并不会变得更好。
子对象不更新的原因不是缺少key或缺少状态,原因是你不遵守不变性原则。
react 的目的是让应用程序更快、响应更快、更容易维护等等,但你必须遵循一些原则。只有在必要时才会重新渲染 React 节点,即如果它们已更新。 react 如何知道组件是否已更新?因为它的状态已经改变。现在不要将它与 setState 挂钩混为一谈。状态是一个概念,每个组件都有它的状态。状态是组件在给定时间点的外观或行为。如果您有一个静态组件,那么您始终只有一个状态,而不必照顾它。如果组件必须以某种方式改变,它的状态就会改变。
现在 react 非常具有描述性。组件的状态可以从一些变化的信息中得出,这些信息可以存储在组件的外部或内部。如果信息存储在内部,则这是组件必须自行跟踪的一些信息,我们通常使用 setState 之类的钩子来管理这些信息。如果此信息存储在我们的组件之外,那么它会存储在不同的组件内部,并且必须跟踪它,即它们的状态。现在其他组件可以通过 props 将它们的状态传递给我们。
这意味着如果我们自己的托管状态发生更改或通过 props 传入的信息发生更改,则 react 会重新渲染。这是自然行为,您不必将道具数据传输到您自己的状态。
现在到了非常重要的一点:react 如何知道信息何时发生变化? 简单:就是进行比较!每当你设置一些状态或给反应一些它必须考虑的信息时,它会将新给定的信息与实际存储的信息进行比较,如果它们不相同,反应将重新呈现该信息的所有依赖关系。
在这方面不一样意味着 javascript === 运算符。
也许你已经明白了。
让我们看看这个:
let a = 42;
let b = a;
console.log('is a the same as b?',a === b); // a and b are the same, right? --> true
a += 5; // now let's change a
console.log('is a still the same as b?',a === b); // --> false
我们正在创建一个值的实例,然后创建另一个实例,将第一个实例的值分配给第二个实例,然后更改第一个实例。
现在让我们看一下对象的相同流程:
let a = { num: 42};
let b = a;
console.log('is a the same as b?',a === b); // a and b are the same, right? --> true
a.num += 5; // now let's change a
console.log('is a still the same as b?',a === b); // --> true
这次的不同之处在于,一个对象实际上是一个指向内存区域的指针,并且在断言 b=a 时,您将 b 设置为与指向完全相同对象的 a 相同的指针。
您在 a 中所做的任何事情都可以通过您的 a 指针或 b 指针访问。
您的线路:
this.props.foo.bar = 123
实际上更新了“this”指向的内存中的一个值。
React 根本无法通过比较对象引用来识别此类更改。您可以将对象的内容更改一千次,并且引用将始终保持不变,并且 react 不会重新渲染相关组件。
这就是为什么您必须将反应中的所有变量视为不可变的。要进行可检测的更改,您需要不同的参考,并且只能通过新对象获得。因此,您不必更改对象,而是必须将其复制到新对象,然后您可以更改其中的一些值,然后再将其移交给做出反应。
看:
let a = {num: 42};
console.log('a looks like', a);
let b = {...a};
console.log('b looks like', b);
console.log('is a the same as b?', a === b); // --> false
扩展运算符(带有三个点的运算符)或 map 函数是将数据复制到新对象或数组的常用方法。
如果您遵守不变性,则所有子节点都会使用新的道具数据进行更新。