跳到主要内容

保持组件纯粹

纯函数

  • 部分 JavaScript 函数是 纯粹 的,这类函数通常被称为纯函数。
  • 纯函数仅执行计算操作,不做其他操作。
  • 你可以通过将组件按纯函数严格编写,以避免一些随着代码库的增长而出现的、令人困扰的 bug 以及不可预测的行为。
  • 但为了获得这些好处,你需要遵循一些规则。
纯函数
// 就是输入什么与输出什么固定。
// 不对传入的props参数进行修改,把传入的参数看成只读。
// 就纯粹的计算或者展示。
function double(number) {
return 2 * number
}

副作用

  • 副作用:(不符合)预期的后果
  • React 的渲染过程必须自始至终是纯粹的。
  • 组件应该只 返回 它们的 JSX,而不 改变 在渲染前,就已存在的任何对象或变量 — 这将会使它们变得不纯粹!
副作用实例
let guest = 0

function Cup() {
// Bad: changing a preexisting variable!
guest = guest + 1
return <h2>Tea cup for guest #{guest}</h2>
}

export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
)
}

  1. 在 React 中,你可以在渲染时读取三种输入:props,state 和 context。你应该始终将这些输入视为只读。
  2. 当你想根据用户输入 更改 某些内容时,你应该 设置状态,而不是直接写入变量。
  3. 当你的组件正在渲染时,你永远不应该改变预先存在的变量或对象。

局部 mutation:组件的小秘密

  • 上述示例的问题出在渲染过程中,组件改变了 预先存在的 变量的值。
  • 为了让它听起来更可怕一点,我们将这种现象称为 突变(mutation) 。
  • 纯函数不会改变函数作用域外的变量、或在函数调用前创建的对象——这会使函数变得不纯粹!
  • 但是,你完全可以在渲染时更改你 刚刚 创建的变量和对象。
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>
}

export default function TeaGathering() {
let cups = []
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />)
}
return cups
}

哪些地方 可能 引发副作用

  • 函数式编程在很大程度上依赖于纯函数,但 某些事物 在特定情况下不得不发生改变。这是编程的要义!
  • 这些变动包括更新屏幕启动动画更改数据等,它们被称为 副作用。它们是 “额外” 发生的事情,与渲染过程无关。
  • 在 React 中,副作用通常属于 事件处理程序
  • 事件处理程序是 React 在你执行某些操作(如单击按钮)时运行的函数。
  • 即使事件处理程序是在你的组件 内部 定义的,它们也不会在渲染期间运行!
  • 因此事件处理程序无需是纯函数。
  • 如果你用尽一切办法,仍无法为副作用找到合适的事件处理程序,你还可以调用组件中的 useEffect 方法将其附加到返回的 JSX 中。
  • 这会告诉 React 在渲染结束后执行它。
  • 然而,这种方法应该是你最后的手段

如果可能,请尝试仅通过渲染过程来表达你的逻辑。你会惊讶于这能带给你多少好处!

React 为何侧重于纯函数?

编写纯函数需要遵循一些习惯和规程。但它开启了绝妙的机遇:

  • 你的组件可以在不同的环境下运行 — 例如,在服务器上!由于它们针对相同的输入,总是返回相同的结果,因此一个组件可以满足多个用户请求。
  • 你可以为那些输入未更改的组件来 跳过渲染,以提高性能。这是安全的做法,因为纯函数总是返回相同的结果,所以可以安全地缓存它们。
  • 如果在渲染深层组件树的过程中,某些数据发生了变化,React 可以重新开始渲染,而不会浪费时间完成过时的渲染。纯粹性使得它随时可以安全地停止计算。
  • 我们正在构建的每个 React 新特性都利用到了纯函数。从数据获取到动画再到性能,保持组件的纯粹可以充分释放 React 范式的能力。

总结

  • 一个组件必须是纯粹的,就意味着:
  • 只负责自己的任务。 它不会更改在该函数调用前就已存在的对象或变量。
  • 输入相同,则输出相同。 给定相同的输入,组件应该总是返回相同的 JSX。
  • 渲染随时可能发生,因此组件不应依赖于彼此的渲染顺序。
  • 你不应该改变任何用于组件渲染的输入。这包括 props、state 和 context。
  • 通过 “设置” state 来更新界面,而不要改变预先存在的对象
  • 努力在你返回的 JSX 中表达你的组件逻辑。当你需要“改变事物”时,你通常希望在事件处理程序中进行。作为最后的手段,你可以使用 useEffect。
  • 编写纯函数需要一些练习,但它充分释放了 React 范式的能力。