- 初阶:
- 是什么:class component 代码重,且复用主要依赖 hoc、mixin,粒度大,代码组织比较混乱等;引入 fc,能让你更简洁定义组件代码,引入 hook,能在 fc 中使用 state 及其它 react 特性
- 优点:
- 更细粒度的逻辑复用,函数 vs 对象
- 单纯从应用角度看,相对比较容易理解 —— 毕竟相当于抛弃了 class 组件的生命周期概念
- 原理简述:hook 底层会将状态挂载到 fiber 节点,在下次更新时就可以拿到上次状态,从而实现有副作用效果的 hook 功能
中阶:实现原理,关键流程
- renderWithHook:关键函数
- 根据 current 节点状态判断是否第一次运行,是的话走 mountState 函数
- 不是第一次运行,走 updateState
mountState:核心逻辑:
- 调用
mountWorkInProgressHook
函数,创建当前 hook 对象:- 如果是第一个 hook:
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
,也就是挂载到 fiber 节点的 memoizedState 链表,作为链表的第一个节点!!! - 如果是后面的hook,则
workInProgressHook = workInProgressHook.next = hook;
,也就是挂载到 fiber 节点 memoizedState 链表的最后一个节点 - fiber.memoizedState 形成链表结构,按 hook 顺序存储每一个 hook 的状态,这很重要!!! ```javascript function mountWorkInProgressHook(): Hook { const hook: Hook = { memoizedState: null,
- 如果是第一个 hook:
baseState: null, baseQueue: null, queue: null,
next: null, };
- 调用
if (workInProgressHook === null) { // This is the first hook in the list currentlyRenderingFiber.memoizedState = workInProgressHook = hook; } else { // Append to the end of the list workInProgressHook = workInProgressHook.next = hook; } return workInProgressHook; } ```
- 绑定 `dispatchSetState` 参数,参数中包含 current 节点,也就形成了一个闭包
dispatchSetState
:setState 函数- 执行 action,计算新 state 值,并将这些更新信息保存到 update 对象中
- 调用
enqueueUpdate
函数将更新任务添加到 hook 更新队列最后 - 调用
scheduleUpdateOnFiber
调度更新任务,最终调用updateState
函数
updateState
:底层调用updateWorkInProgressHook
,这里关键逻辑就是按照调用顺序,在memoiedState
中找到对应的 hook 记录 ```javascript export function xx(){ const [data, setData] = useState(null); const [age, setAge] = useState(10); }
- renderWithHook:关键函数
fiber.memoizedState === hook[data] hook[data].next === hook[age] ```
- 之后,`updateReducer` 中循环执行所有 update 任务,计算出最新 state 值
- state 值保存到 hook.memoridState 变量,完成更新
- react 会用最新的 state 值计算 vdom,实现状态更新
- 高阶:
- 为什么不能在循环、if 等语句中调用 hook?为了确保每次 render 都是按相同的顺序执行 hook
- 因为会导致
memoiedState
链表结构发生变化,mount 与 update 就对不上了,很容易出问题
- 因为会导致
- 与函数式编程理念冲突吗?我认为不
- 函数式 !== 纯函数,副作用基本上是不可避免的
- 在 fp 中,副作用一般通过 functor 等方式实现,目标是将副作用控制在有限范围内
- 与 Vue 相比:
- 共同点:都用于实现更细粒度逻辑复用;都能够实现有副作用的状态管理;
- 区别:
- react 任意 state 变更 —— 无论实际有没有参与组件渲染,都会导致组件重新 render;vue 则有响应式加持,仅当 render 函数依赖的属性发生变更时才重新渲染
- react 会重复执行,hook 也会重复执行,需要用 useCallback 等缓存函数;vue setup 只会执行一次,hook 也只会有一次,gc 压力较小,也不需要考虑函数缓存
- react 的副作用通过把状态挂载到 fiber 节点上;vue 则是给数据加上响应式能力
- vue 对调用顺序没什么要求;react 有严格要求
- react hook 依赖需要手动声明;vue 不需要
- 最佳实践:
- state 初始化函数
useState(initFunc)
只会执行一次,所以 state 值不应该依赖 props 等可变参数 - 绝对不要在 for 、if 语句中执行hook;尽量保持在 render 函数顶层作用域执行hook
- state 初始化函数
- 为什么不能在循环、if 等语句中调用 hook?为了确保每次 render 都是按相同的顺序执行 hook
资料: