一、 Hook使用规则
-
只在最顶层使用Hook
-
不要在循环,条件或嵌套函数中调用Hook;
-
只在组件函数和自定义hook中调用Hook
Q1 : 为什么 hook 不能 在循环,条件或嵌套函数中调用Hook ?
A1: 因为这跟React的渲染函数和React Hook的实现原理有关,如果在循环,条件或嵌套函数中调用hook,会影响到了React自身记录的Hook顺序,会导致组件状态(值)不一致问题。
Q2: 为什么组件内,刷新了一次后,useState 仍然能保持最新的值,而不是回到初始值?
A2: 这是因为,useState 这个hook底层逻辑是利用闭包原理,它会把最新的值,初始化的值,下一个要调用的hook这些值创建成一个fiber对象,后面每次刷新都会从这个fiber对象上获取。
二、 简单实现一个 useState
demo1 : 问题在每次刷新,值都是初始化
function useState(initialValue) {var state = initialValue;function setState(newState) {state = newState;render();}return [state, setState];
}
demo2 : 问题在,如果组件内使用多个useState, _state 值会被覆盖
var _state; // 把 state 存储在外面function useState(initialValue) {_state = _state || initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它function setState(newState) {_state = newState;render();}return [_state, setState];
}
demo3: 按照hook的顺序,把state值依次存进 memoizedState 中 。
这就是为什么 hook 不能 在循环,条件或嵌套函数中调用Hook , 因为这样会影响React自身记录的Hook顺序。
let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标function useState(initialValue) {memoizedState[cursor] = memoizedState[cursor] || initialValue;const currentCursor = cursor;function setState(newState) {memoizedState[currentCursor] = newState;render();}return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}
三、 真正实现一个 useState
虽然我们用数组基本实现了一个可用的 Hooks,了解了 Hooks 的原理,但在 React 中,实现方式却有一些差异的。
-
React 中是通过类似单链表的形式来代替数组的。通过 next 按顺序串联所有的 hook。
type Hooks = {memoizedState: any, // 指向当前渲染节点 FiberbaseState: any, // 初始化 initialState, 已经每次 dispatch 之后 newStatebaseUpdate: Update<any> | null,// 当前需要更新的 Update ,每次更新完之后,会赋值上一个 update,方便 react 在渲染错误的边缘,数据回溯queue: UpdateQueue<any> | null,// UpdateQueue 通过next: Hook | null, // link 到下一个 hooks,通过 next 串联每一 hooks }type Effect = {tag: HookEffectTag, // effectTag 标记当前 hook 作用在 life-cycles 的哪一个阶段create: () => mixed, // 初始化 callbackdestroy: (() => mixed) | null, // 卸载 callbackdeps: Array<mixed> | null,next: Effect, // 同上 };
-
memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的?
我们知道,react 会生成一棵组件树(或Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,伴随组件一起出生,一起死亡。
参考:
【React全解3】React.useState原理详解,一次性搞懂useState_react usestate-CSDN博客
React Hooks 原理 · Issue #26 · brickspert/blog · GitHub