大家好,我卡颂。
5月30日刚好是React
10周年纪念日。
我顺手拉了下React
最新代码,这一看不要紧,居然已经有22个hook
了。
其中:
-
react
包导出了21个 -
react-dom
包导出了1个(useFormStatus
)
本文会从React
这些年发展脉络的角度,聊聊这些hook
的作用。
欢迎加入人类高质量前端交流群,带飞
时代的更迭
截止当前,React
的发展主要经历了3个时期:
-
CSR
时期(客户端渲染时期) -
并发时期
-
RSC
时期(服务端组件时期)
当前的22个hook
也都是这3个时期的产物。
CSR
时期
时间回到2013年,为了解决facebook
日益复杂的交互,jordwalke开发了React
。经过一段时间摸索,React
逐渐形成一套满足CSR
的开发模式。
这套开发模式从ClassComponent
迁移到FunctionComponent
后,便形成了最初的一批hook
。这些hook
都与CSR
的开发模式相关。比如:
与状态的流转相关的:
-
useState
-
useReducer
-
useContext
与处理副作用相关的:
-
useEffect
-
useLayoutEffect
与提高操作自由度相关的:
useRef
与性能优化相关的:
-
useMemo
-
useCallback
与调试相关:
useDebugValue
随着React
持续迭代,又引入了几个hook
,本质来说他们都是为了完善CSR
的开发模式,对现有hook
能力进行补充或约束:
-
useImperativeHandle
(控制useRef
防止其失控) -
useEffectEvent
(对useEffect
能力的补充) -
useInsertionEffect
(对useEffect
场景的补充) -
useMemoCache(减少性能优化心智负担)
这里简单聊聊useMemoCache
。长久以来,不管是ClassComponent
的shouldComponentUpdate
,还是FC
中2个性能优化相关hook
,都存在比较重的心智负担,比如:
-
开发者需要考虑是否需要性能优化
-
开发者需要考虑何时使用
useMemo
、useCallback
为了解决这个问题,在2021年的React Conf
,黄玄带来了能够通过编译器生成等效于useMemo、useCallback代码的方案 —— React Forget
。
useMemoCache
就是React
内部为React Forget
提供缓存支持的hook
。
所以这个hook
是给编译器用的,而不是我们普通开发者。
并发时期
在13年诞生之初,React
的作者jordwalke就指出 —— React
未来会发展并发特性。
这并不是什么高瞻远瞩的预言,React
本身是个重运行时的框架,这意味着他的迭代方向需要围绕运行时展开。而并发特性是一种优秀的运行时性能优化策略。
随着并发特性落地,首先推出的是2个并发相关hook
:
-
useTransition
-
useDeferredValue
这2个hook
的本质都是降低更新的优先级,更新意味着视图渲染,所以当更新拥有不同优先级后,这意味着视图渲染拥有不同优先级。
这就是并发更新的理论基础。
但是,并发更新的出现,打破了React
沿袭多年的一次更新对应一次渲染的模式。
为了让现有的库兼容并发模式,推出了如下hook
:
-
useMutableSource
-
useSyncExternalStore
所以,上述2个hook
主要是面向开源库作者。
RSC
时期
RSC
(服务端组件)是一个浩大的工程,他的实现不是一蹴而就的,这一点从新出的hook
就能看出。
既然是服务端组件,那就涉及到组件在服务端渲染。那么,对于存在唯一标识(比如下面的id props
)的组件,如何保证这个唯一标识在服务端与客户端一致呢?
<SomeCpn id={id}/>
如果组件仅在一端渲染,简单使用Math.random()
就能获得唯一标识:
const id = Math.random();
<SomeCpn id={id}/>
但如果这段逻辑在服务端/客户端都运行一次,显然id
就不唯一了。
为了生成在服务端/客户端唯一的id
,有了:
useId
在并发时期,由于引入了渲染优先级的概念,那势必存在一些由于优先级不足,而处于pending
中的渲染。
如何展示渲染的pending状态呢?React
引入了<Suspense>
组件。
到了RSC
时期,React
团队发现,渲染的pending状态是pending
,数据请求的pending状态不也是pending
吗?
换言之,任何需要中间pending
状态的流程,不都可以纳入<Suspense>
的管理范围?
那该怎么标记一个流程可以被纳入<Suspense>
的管理呢?于是有了:
use
通过这个hook
声明的流程中的pending
状态都会被纳入<Suspense>
的管理。
既然<Suspense>
越来越重要,那我们是不是要针对他做些优化?既然<Suspense>
可以在不同视图之间切换,那为他增加缓存显然是种不错的优化方式,于是有了:
- useCacheRefresh(用于建立
<Suspense>
缓存)
到这一步,RSC
的基础设施算是搭好了,下一步该构建上层应用了。
在浏览器端,与RSC
理念最契合的便是form
标签,围绕form
标签的action
属性,React
推出了如下hook
:
-
useOptimistic
-
useFormStatus
这2个hook
都是为了优化表单提交这一场景(也可以说是RSC
与客户端的交互场景)。
关于这2个
hook
,更详细的解释可以参考form 元素是 React 的未来一文
总结
如果说CSR
时期的hook
都是面向开发者直接使用的。那么并发时期最初的2个hook
(useTransition
、useDeferredValue
)已经鲜有开发者使用了,而后期类似useMutableSource
这样的hook
,普通开发者则根本用不到。
同样的,再往后的RSC
时期的所有hook
,普通开发者都用不到。他们都是为其他库、框架(比如Next.js
)提供的。
这标志着React
发展方向的不断变化:
-
早期,定位是前端框架,主要为了解决
facebook
自身问题,顺便开源,受众是开发者 -
中期,定位是底层
UI
库,受众是开源库作者 -
当前,定位是
web
底层操作系统,受众是上层全栈框架