useEffect 的 第二个参数
会对对象进行 浅层 比较来判断是否执行 useEffect 中的副作用代码,不会递归比较对象内部的属性。如果对象的引用没有变化,即便对象内部的属性发生变化,useEffect 也不会重新执行,
- 可以考虑使用
useEffect内部的清理函数来手动检查变化;通过一个中间变量 通过取反,return - 使用useMemo来确保每次渲染都返回一个新的对象
实现原理
useEffect 是 React 中的一个 Hook,用于处理副作用。其实现原理涉及到 React 的渲染流程和生命周期,以及 JavaScript 的异步执行机制。
以下是 useEffect 的基本实现原理:
-
调度和执行:
- 当函数组件渲染时,React 会将
useEffect中的回调函数添加到一个任务队列中,该队列在渲染结束后被执行。 - React 通过调度器(Scheduler)来安排副作用的执行。调度器是 React 内部的模块,负责管理任务的执行时机。
- 当函数组件渲染时,React 会将
-
副作用的执行时机:
- 默认情况下,
useEffect中的副作用会在每次组件渲染之后执行。这意味着它在渲染阶段之后、布局阶段之前执行。 - 如果
useEffect的第二个参数是空数组[],则副作用只会在组件挂载和卸载时执行,相当于类似于类组件的componentDidMount和componentWillUnmount。 - 如果
useEffect的第二个参数包含依赖项数组,它会在依赖项发生变化时执行。
- 默认情况下,
-
清理函数:
- 如果
useEffect返回一个函数,该函数会在组件卸载或下一次useEffect执行前执行,用于清理副作用。 - 清理函数主要用于取消订阅、清除定时器等清理操作。
- 如果
-
优化:
- React 会尽量在浏览器空闲时执行副作用,以避免阻塞主线程,提高性能。
- 通过调度器和调度优先级的概念,React 可以在不同的优先级下调度任务,确保高优先级的任务先执行。
useState的实现原理,怎么通过自定义hooks来实现useState
useState 的实现原理基于 Fiber 架构
-
Fiber 架构:
- React 16 引入了 Fiber 架构,用于实现可中断、可恢复的渲染。
- Fiber 架构中,工作单元(Fiber)表示组件的渲染工作。
-
Hooks 存储在 Fiber 节点中:
- 在每个 Fiber 节点上,都有一个 Hooks 链表,用于存储该组件使用的所有 Hooks。
useState使用了这个 Hooks 链表来保存状态。
-
useState 的具体实现:
- 当组件渲染时,React 会遍历 Fiber 树,找到当前组件的 Fiber 节点。
- 在 Fiber 节点上,有一个 Hooks 链表,每个节点都包含了当前组件使用的所有 Hooks 的信息。
- 当执行到
useState时,React 会在当前组件的 Fiber 节点上查找是否已经有一个对应的useState的 Hook。如果有,就直接使用之前的状态;如果没有,就创建一个新的 Hook 并添加到 Hooks 链表中。
-
useState 返回值:
useState返回一个包含当前状态和更新状态的数组[state, setState]。state是当前状态的值,setState是一个更新状态的函数。
-
useState 的更新触发重新渲染:
- 当调用
setState时,React 会标记组件为“脏”(dirty),表示需要重新渲染。 - 在下一次渲染时,React 会重新调用组件函数,通过 Hooks 链表恢复之前保存的状态值。
- 当调用
-
闭包保存状态:
- 由于 Hooks 在函数组件中是按顺序调用的,React 通过闭包的方式,保证每次调用
useState都能正确访问到对应的状态
- 由于 Hooks 在函数组件中是按顺序调用的,React 通过闭包的方式,保证每次调用
下面是一个简化的伪代码,用于演示 useState 的实现原理:
let currentComponent; // 当前组件的 Fiber 节点
function useState(initialState) {
const hooks = currentComponent.memoizedState; // 当前组件的 Hooks 链表
if (!hooks) {
// 如果当前组件没有 Hooks 链表,创建一个
currentComponent.memoizedState = {
queue: [], // 保存状态更新的队列
baseState: initialState, // 初始状态值
baseQueue: null, // 上一次的状态更新队列
};
hooks = currentComponent.memoizedState;
}
// 获取当前 Hook 的索引
const hookIndex = hooks.queue.length;
// 保存状态和更新状态的函数到 Hooks 链表
const state = hookIndex < hooks.baseQueue.length
? hooks.baseQueue[hookIndex]
: hooks.baseState;
// 更新状态的函数
const setState = action => {
hooks.queue.push(action); // 将状态更新添加到队列
scheduleWork(currentComponent); // 标记组件为“脏”,准备重新渲染
};
return [state, setState];
}