React 官方提出了两条使用 Hook 的规则
- 只在最顶层使用 Hook,不要在循环,条件或嵌套函数中调用 Hook
确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。 - 只在 React 函数中调用 Hook,不要在普通的 JavaScript 函数中调用 Hook
如果偏不遵守呢?
export default function App() {
if (Math.random() > 0.5) {
useState(10000);
}
const [value, setValue] = useState(0);
return (
<div>
<button onClick={() => setValue(value + 1)}>+</button>
{value}
</div>
);
像上面这种情况,当你点击的时候,
更具体一点的情况就是,有时候状态不满足我们预期条件的时候,希望通过 if else 去控制更新。很遗憾这样是不可以的。
为什么要做出这种限制
因为,React 它是通过链表去实现 hooks 的调用的。也可以简单理解为通过数组下标去访问实现的。本质上就是,去顺序调用不同的 hooks。第一个 hook 执行完了之后,通过 .next 指向下一节点,然后就可以执行下一 hook。
为什么顺序执行对 hook 很重要
就很多人下意识肯定都会想,那react为什么不用key做映射呢,类似与 Map 类型,每个 hook 都用唯一的key对应,这样的话不就可以不用顺序调用了吗?
很关键的一点就是,你使用 key 值取映射 hook 的话。那么自定义的 hook 被多个组件调用的话,你很难不保证之前有没有同名的key在其他组件内。
那我要是用 Symbol 呢,这样就不重复了吧。很遗憾,那就不能很好地复用了,因为 key 值的唯一性使得总是同一个 key调用了 hook。
Event Loop执行顺序
- 一开始整个脚本作为一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
- 执行浏览器UI线程的渲染工作
- 检查是否有Web Worker任务,有则执行
- 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空
宏任务:script 、setTimeout、setInterval 、setImmediate 、I/O 、UI rendering
微任务:MutationObserver、Promise.then()或catch()、fetch API、V8的垃圾回收过程、Node独有的process.nextTick