Hook
当组件需要记住下拉菜单是否打开,或者需要在渲染后运行代码来与服务器同步时,这些都是 Hook 的用途。你已经用过两个了:useState 让组件拥有记忆,useEffect 在渲染后运行代码来与 React 外部交互。这两个是你最常用的,其他内置 Hook 也遵循同样的模式:你在组件内调用一个函数,它就会让你接入 React 的某个功能。这个函数的名字总是以 use 开头,这个前缀就是 Hook 的全部定义。
常见内置 Hook 速览
你不需要在第一天就掌握所有 Hook,但了解有哪些工具可用会很有帮助,这样当遇到问题时你就能识别出正确的工具。以下是最常用的几个:
useRef持有一个在多次渲染间保持不变的可变值,并且不会触发新的渲染。当你需要直接访问 DOM 元素时(比如让一个输入框获得焦点),也可以用它。useContext从周围的 provider 读取共享数据,这样深层嵌套的组件就能获取到值,而不需要你逐层传递 props。useReducer通过 reducer 函数来管理状态,每次更新都描述从当前状态到下一个状态的转换。当状态变化变得复杂到单个useState难以应付时,这是个很好的选择。useMemo在多次渲染间缓存一个计算结果,只在输入改变时才重新计算。useCallback对函数做同样的事,在多次渲染间返回同一个函数实例,而不是每次都创建一个新的。
useMemo 和 useCallback 在记忆化章节有更深入的讨论,那里会讲性能优化的故事。Ref 和 DOM 会全面介绍 useRef,Context 则会从本章的总结深入到 provider 和共享状态。对于 useReducer,这一章是最深入的讲解,需要复习时可以回来查看。现在的重点是这个模式:Hook 是你进入某个 React 功能的入口,它的名字就告诉你这是一个 Hook。
Hook 的规则
Hook 有两条规则,React 依赖你遵守它们。
首先,在组件的顶层调用 Hook。不要在条件、循环或嵌套函数中调用它们。组件使用的每个 Hook 都应该在每次渲染时以相同的顺序运行。
function Profile({ isLoggedIn }) {
// 错误:这个 Hook 只在某些时候运行
if (isLoggedIn) {
const [name, setName] = useState('')
}
// 正确:在顶层无条件地调用它
const [name, setName] = useState('')
// ...然后在逻辑中使用 isLoggedIn
}其次,只从 React 函数组件或其他自定义 Hook 中调用 Hook。普通的 JavaScript 函数不能使用 Hook,因为 React 没有组件渲染来附加状态。
编写自己的 Hook
假设有几个组件需要知道浏览器窗口的宽度。与其在每个组件中复制相同的 useState 和 useEffect 代码,你可以把这个逻辑封装一次:
import { useState, useEffect } from 'react'
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return width
}现在任何组件都可以调用 const width = useWindowWidth(),得到一个在窗口变化时更新的值。useWindowWidth 是一个自定义 Hook:一个调用其他 Hook 的函数。这就是全部模式,因为它是一个函数,你可以给一段有状态的逻辑起个名字,放在一个地方,然后在组件间重用。use 前缀是标记它为 Hook 的方式,既让 React 的工具能识别,也让下一个读代码的人知道这是一个 Hook。自定义 Hook 把你已有的 Hook 组合成可重用的东西,不会给 React 增加新的能力。
use 开头的函数,它让你的组件接入像记忆或副作用这样的 React 功能。你已经见过 useState 和 useEffect,其他的也遵循同样的模式。 要记住的主要点:在组件顶部调用 Hook,永远不要在 if 或循环里调用,这样它们就能在每次渲染时以相同的方式运行。
接下来:用 React 思考,在那里组件、props、状态和副作用会聚在一起,形成一个把设计转化为 UI 的过程。

