react 之 Hook 简述
从
React 16.8+
新增一批,以use前缀开头的函数,让函数组件也可以使用state
以及其他的React
特性。 本文简述这批钩子函数(hook
)一些使用技巧及应用场景, 并试图探究让hook
节点结构队列和它的状态可以在外部定位原理
Hook概览
useState
函数useState
返回一对值:当前状态和一个用于更新它的函数- 可以在事件处理函数中或其他一些地方调用这个函数, 类似
class
组件的this.setState
- 但它不会把新的
state
与旧的state
进行合并
- 但它不会把新的
- 声明多个
state
变量, 使用数组解构语法,可给 state 变量取不同的名字
State Hook
- 在函数组件内钩入一些
React State
及生命周期等特性的函数 - Hook 来复用不同组件之间的状态逻辑,通常只用在函数组件
Effect Hook
- 在
React
组件中执行过数据获取、订阅或者手动修改过DOM
等操作,谓之"副作用" useEffect
函数 给函数组件增加了操作副作用的能力- 默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候
- 调用
useEffect
时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
- 副作用函数还可以通过返回一个函数来指定如何“清除”副作用
- 与
useState
一样,可在组件中多次使用useEffect
- 与
Hook 规则
- Hook 就是 JavaScript 函数,有两个额外规则
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
- 只能在
React
的函数组件中调用Hook
(自定义的Hook
中也可)
- 官方提供
linter
插件来自动执行这些规则
自定义Hook
- 在组件之间重用一些状态逻辑
- 当前两种主流方案:高阶组件和
render props
- 自定义
Hook
可以让你在不增加组件的情况下达到同样的目的
- 当前两种主流方案:高阶组件和
Hook
是一种复用状态逻辑的方式,它不复用state
本身- 自定义Hook类似于一种约定而不是功能
- 若函数的名字以use开头并调用其他Hook,则称之为自定义Hook
useSomething
的命名约定可以让linter
插件在使用Hook
的代码中找到bug
- 应用场景
- 表单处理,动画,订阅声明,计时器
其他Hook
useContext
不使用组件嵌套就可以订阅React
的Context
useReducer
通过reducer
来管理组件本地的复杂state
Hook系统关键
确保
Hook 在React
作用域内使用
Dispatcher
Dispatcher
是一个包含了hook
函数的共享对象- 基于
ReactDOM
的渲染状态,它将会被动态的分配或者清理,并且它将会确保用户不能在React
组件之外获取到hook
- 具体做法 通过一个名为
enableHooks
的标志来启用/禁用hook
,当完成渲染工作后,React
会废弃当前的dispatcher
并禁止hook
- 具体做法 通过一个名为
- 基于
Dispatcher
在每次hook
的调用中都会被函数resolveDispatcher()
解析
Hook 队列
在 React 后台,hook 会被表示为节点,并以调用顺序连接起来。 hook 并不是被简单的创建然后丢弃,它们有一套独有的机制,一个 hook 会有数个属性
执行流程
- 在初次渲染的时候,它的初始状态会被创建
- 它的状态可以在运行时更新
- React 可以在后续渲染中记住 hook 的状态
- React 能根据调用顺序提供正确的状态
- React 知道当前 hook 属于哪个部分
-
React 状态视角
通常只把组件状态看作一个简单的对象,但当处理 hook 的时候,状态需要被看作是一个队列,每个节点都表示了对象的一个模块,其hook节点简易结构如下{ memoizedState: 'foo', next: { memoizedState: 'bar', next: { memoizedState: 'bar', next: null } } }
hook 运行的关键代码在于
memoizedState
和next
,其他(如baseState,baseUpdate,queue
辅助性)的属性会被useReducer() hook
使用,来缓存发送过的 action 以及基本的状态。在每个函数组件调用前,一个名为 prepareHooks() 的函数将先被调用,在这个函数中,当前结构和 hook 队列中的第一个 hook 节点将被保存在全局变量中。以便达到任何时候调用hook
函数(useXXX()),都能知道当前运行的上下文 -
Hook 队列的实现逻辑
一旦更新完成,finishHook()
所在的函数将会被调用,在这个函数中,hook 队列的第一个节点的引用将会被保存在渲染了的结构memoizedState
属性中,从外部读取某一组件状态变迁 -
State Hook
useState
这个hook
在后台使用了useReducer
,并且它将useReducer
作为预定义的reducer
这意味着,useState
返回的结果实际上已经是reducer
的状态,同时也是action dispatcher
Hook索引
- 基础 Hook
- useState
- useEffect
- useContext
- 额外的 Hook
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
本作品采用《CC 协议》,转载必须注明作者和本文链接