保持组件纯粹
有些 JavaScript 函数是纯粹的。纯函数只执行计算,不做其他任何事情。通过严格地将组件编写为纯函数,你可以避免随着代码库增长而出现的一整类令人困惑的 bug 和不可预测的行为。但要获得这些好处,你必须遵循一些规则。
您将学习
- 什么是纯粹性以及它如何帮助你避免 bug
- 如何通过避免在渲染阶段进行更改来保持组件纯粹
- 如何使用严格模式查找组件中的错误
纯粹性:组件即公式
在计算机科学(尤其是函数式编程领域)中,纯函数是具有以下特征的函数:
- 它只管自己的事。它不会更改在调用之前已存在的任何对象或变量。
- 相同的输入,相同的输出。给定相同的输入,纯函数应始终返回相同的结果。
你可能已经熟悉纯函数的一个例子:数学中的公式。
考虑这个数学公式:y= 2x。
如果x= 2,那么y= 4。总是如此。
如果x= 3,那么y= 6。总是如此。
如果x= 3,y不会有时是9或–1或2.5,这取决于一天中的时间或股市的状态。
如果y= 2x且x= 3,那么y将始终是6。
如果我们将其转换为 JavaScript 函数,它看起来会是这样:
在上面的例子中,double是一个纯函数。如果你传入3,它将返回6。总是如此。
React 就是围绕这个概念设计的。React 假设你编写的每个组件都是纯函数。这意味着你编写的 React 组件在给定相同输入时必须始终返回相同的 JSX:
当你将drinkers={2}传递给Recipe时,它将返回包含2 cups of water的 JSX。总是如此。
如果你传递drinkers={4},它将返回包含4 cups of water的 JSX。总是如此。
就像数学公式一样。
你可以将组件视为食谱:如果你遵循它们,并且在烹饪过程中不引入新的食材,你每次都会得到相同的菜肴。那个“菜肴”就是组件提供给 React 用于渲染的 JSX。

插图作者:Rachel Lee Nabors
副作用:(非)预期的后果
React 的渲染过程必须始终是纯粹的。组件应该只返回它们的 JSX,而不应更改渲染之前已存在的任何对象或变量——否则会使它们变得不纯粹!
这是一个违反此规则的组件:
该组件正在读取和写入在其外部声明的guest 变量。这意味着 多次调用此组件将产生不同的 JSX!更糟糕的是,如果其他 组件读取 guest,它们也会产生不同的 JSX,具体取决于它们何时被渲染!这是不可预测的。
回到我们的公式 y= 2x,现在即使x= 2,我们也不能相信y= 4。我们的测试可能会失败,用户会感到困惑,飞机可能会从天上掉下来——你可以看到这会导致多么令人困惑的错误!
你可以通过 将 guest 作为 prop 传递来修复此组件:
现在你的组件是纯的,因为它返回的 JSX 仅取决于guestprop。
一般来说,你不应该期望你的组件以任何特定顺序渲染。无论你在 y= 2x之前还是之后调用y= 5x都无关紧要:两个公式将彼此独立地求解。同样,每个组件应该只“为自己思考”,而不应在渲染期间尝试与其他组件协调或依赖它们。渲染就像学校考试:每个组件都应该独立计算 JSX!
局部突变:组件的小秘密
在上面的例子中,问题在于组件在渲染时更改了一个预先存在 的变量。这通常被称为 “突变”,听起来有点吓人。纯函数不会改变函数作用域之外的变量或在调用之前创建的对象——否则它们就是不纯的!
然而,更改你在渲染过程中 刚刚创建的变量和对象是完全没问题的。在这个例子中,你创建了一个[]数组,将其赋值给一个cups 变量,然后 push一打杯子进去:
如果 cups 变量或 [] 数组是在 TeaGathering函数外部创建的,这将是一个大问题!你将通过向该数组中推送项目来更改一个预先存在 的对象。
然而,这没关系,因为你在 同一次渲染期间、在 TeaGathering内部创建了它们。TeaGathering之外的任何代码都不会知道这件事。这被称为“局部突变”——它就像是你的组件的小秘密。
你 可以引发副作用的地方
虽然函数式编程非常依赖纯度,但在某些时候,某个地方,某些东西必须改变。这某种程度上就是编程的意义所在!这些变化——更新屏幕、启动动画、更改数据——被称为副作用。 它们是 “在侧面”发生的事情,而不是在渲染期间。
在 React 中,副作用通常属于 事件处理程序。事件处理程序是当你执行某些操作(例如单击按钮)时 React 运行的函数。尽管事件处理程序是在你的组件内部定义的,但它们不会在渲染期间运行!因此事件处理程序不需要是纯的。
如果你已经用尽了所有其他选项,并且找不到适合你副作用的事件处理程序,你仍然可以通过在组件中使用useEffect调用将其附加到返回的 JSX 上。这会告诉 React 稍后在渲染之后、允许副作用时执行它。然而,这种方法应该是你最后的手段。
在可能的情况下,尝试仅通过渲染来表达你的逻辑。你会惊讶于这能带你走多远!
回顾
- 组件必须是纯的,这意味着:
- 它只管自己的事。它不应该改变渲染前已存在的任何对象或变量。
- 相同的输入,相同的输出。给定相同的输入,组件应始终返回相同的 JSX。
- 渲染可能随时发生,因此组件不应依赖于彼此的渲染顺序。
- 你不应该改变组件用于渲染的任何输入。这包括 props、state 和 context。要更新屏幕,请“设置”状态而不是改变预先存在的对象。
- 努力在你返回的 JSX 中表达组件的逻辑。当你需要“改变事物”时,通常希望在事件处理程序中完成。作为最后的手段,你可以使用
useEffect。 - 编写纯函数需要一点练习,但它释放了 React 范式的力量。
尝试一些挑战
Challenge 1 of 3:修复损坏的时钟 #
该组件尝试在午夜到早上六点之间将 <h1> 的 CSS 类设置为 "night",在其他时间设置为 "day"。然而,它没有正常工作。你能修复这个组件吗?
你可以通过临时更改计算机的时区来验证你的解决方案是否有效。当当前时间在午夜到早上六点之间时,时钟应该显示反转的颜色!
