React 有十几个 hooks,看得头大。但实际上必须学的只有四个。
今天就来分个类。
按照“需要记忆”的等级,分成三类——
少了它们,React 功能不完备:
useState - 少了它,人就没法跟网页交互了useEffect - 少了它,没法和外界同步useContext - 少了它,同级组件之间没法传递属性useRef - 少了它,没法访问 HTML 元素、有时候没办法访问最新状态值因为没办法替代,所以几乎每个应用都会用到。
useMemo - 缓存结果useCallback - 缓存函数useTransition - 允许不重要的工作推迟、被打断useDeferredValue - 延迟更新useReducer - 复杂状态管理(可以用 useState 实现)useActionState - 表单状态管理(可以用 useState 实现)useEffectEvent - 一个东西会变化,但不想因为它引起组件重新渲染(可以用 useRef 实现)但实际上,像 useActionState 确实非常好用。一个复杂表单写十来个状态,代码太丑了也不行。后面会专门写一篇。
React 的组件是“纯”(pure)函数:只要参数一样,每次渲染就一样。这是它概念简单之处。但真这样网页就是死的了(静态),可现在的网页又都讲究生动(reactive)。
所以简单概念必须有例外——组件的状态是保存在组件外面的。就像是隐藏的参数,每次变了都会导致组件重新渲染(“画在网页上”)。这个保存的机制就是钩子——钩住一个个外部的稳定变量(简称“外稳变量”;虽然可以简单记成“全局变量”,但细究起来这俩不是一回事)。
最常见的钩子是useState,setStateA 函数就是在修改外稳变量(不妨叫它 stateA)。每次 stateA 改动,都会触发组件重新渲染。
这里要说清楚:是 stateA 导致了组件重新渲染;而不是重新渲染这个动作导致 stateA 变化/重置。其他变量就不一样了,比如let a = 1。不管 a 在组件内发生了什么变化,重新渲染都会导致它被重置成 1。
从这个意义上,所有的钩子都是“跨渲染”不变的。
这里头又有一个很特殊。它很像 useState,可以跨渲染保存状态,也可以主动改变状态值,却不会因为值的变化而导致重新渲染:useRef。——这又是因为它是唯一一个指针型钩子——钩住的是内存中变量的位置,而不是这个位置存储的具体数值。
useRef 特别适合那些需要关注最新的值,但又不需要把值显示在页面上的那些关键/底层状态(比如某个播放器窗口元素,上一个状态值,当前的项目 id 等等)。
和几个常见的钩子的对比如下:
| useState | ||||
| useReducer | ||||
| useContext | ||||
| useRef | ||||
| useEffect | ||||
| useEffectEvent | ||||
| useMemo | ||||
| useCallback | ||||
| useTransition | ||||
| useActionState | ||||
| 普通变量 |
再次注意,
useRef 是“可变的”(此时,它所“钩住”的地址本身没有变化;只是地址里存储的值变了);这个特性可以用来解决“闭包陷阱”——这就是另外一个复杂问题了。下一篇再详细写。