模拟实现 useEffect 功能
useEffect
是 React 的核心 Hook 之一,用于处理副作用操作。下面我将模拟实现一个简化版的 useEffect
,帮助你理解其工作原理。
基础实现
let currentComponent = null;
let effectIndex = 0;
let previousDeps = [];function useState(initialValue) {// 简化的 useState 实现const state = currentComponent._states[effectIndex] || initialValue;const setState = (newValue) => {currentComponent._states[effectIndex] = newValue;// 触发重新渲染renderComponent(currentComponent);};effectIndex++;return [state, setState];
}function useEffect(callback, deps) {const currentIndex = effectIndex;const hasNoDeps = !deps;const hasChangedDeps = previousDeps[currentIndex] ? !deps.every((dep, i) => dep === previousDeps[currentIndex][i]): true;if (hasNoDeps || hasChangedDeps) {// 清理上一次的 effectif (currentComponent._cleanups[currentIndex]) {currentComponent._cleanups[currentIndex]();}// 执行新的 effectconst cleanup = callback();currentComponent._cleanups[currentIndex] = cleanup;previousDeps[currentIndex] = deps;}effectIndex++;
}// 简化的组件模型
class Component {constructor() {this._states = [];this._cleanups = [];this._effectIndex = 0;}render() {currentComponent = this;effectIndex = 0;// 组件渲染逻辑...}
}function renderComponent(component) {// 重置 effect 索引component._effectIndex = 0;// 执行清理函数component._cleanups.forEach(cleanup => cleanup && cleanup());component._cleanups = [];// 重新渲染component.render();
}
完整版实现(更接近 React 实际工作方式)
// 全局变量存储当前渲染的 Fiber 节点
let currentlyRenderingFiber = null;
let workInProgressHook = null;
let currentHook = null;// 模拟 Fiber 节点结构
class FiberNode {constructor() {this.memoizedState = null; // 存储 hooks 链表this.alternate = null; // 指向当前或 work-in-progress 树this.updateQueue = []; // 更新队列}
}// 模拟 Hook 结构
class Hook {constructor() {this.memoizedState = null; // 存储状态this.next = null; // 下一个 Hookthis.queue = null; // 更新队列}
}// 模拟 Effect Hook
class Effect {constructor(create, destroy, deps) {this.create = create;this.destroy = destroy;this.deps = deps;this.next = null;}
}function useEffect(create, deps) {const hook = mountWorkInProgressHook();const nextDeps = deps === undefined ? null : deps;// 标记当前 Fiber 有副作用需要处理currentlyRenderingFiber.effectTag |= Update | Passive;hook.memoizedState = pushEffect(HookHasEffect | HookPassive,create,undefined,nextDeps);
}function mountWorkInProgressHook() {const hook = new Hook();if (workInProgressHook === null) {// 这是第一个 HookcurrentlyRenderingFiber.memoizedState = workInProgressHook = hook;} else {// 添加到链表末尾workInProgressHook = workInProgressHook.next = hook;}return workInProgressHook;
}function pushEffect(tag, create, destroy, deps) {const effect = new Effect(create, destroy, deps);effect.tag = tag;if (currentlyRenderingFiber.updateQueue === null) {currentlyRenderingFiber.updateQueue = createFunctionComponentUpdateQueue();currentlyRenderingFiber.updateQueue.lastEffect = effect.next = effect;} else {const lastEffect = currentlyRenderingFiber.updateQueue.lastEffect;if (lastEffect === null) {currentlyRenderingFiber.updateQueue.lastEffect = effect.next = effect;} else {const firstEffect = lastEffect.next;lastEffect.next = effect;effect.next = firstEffect;currentlyRenderingFiber.updateQueue.lastEffect = effect;}}return effect;
}function createFunctionComponentUpdateQueue() {return {lastEffect: null,};
}// 模拟 commit 阶段处理 effects
function commitPassiveEffects(finishedWork) {const updateQueue = finishedWork.updateQueue;if (updateQueue !== null) {const lastEffect = updateQueue.lastEffect;if (lastEffect !== null) {const firstEffect = lastEffect.next;let effect = firstEffect;do {if ((effect.tag & HookPassive) !== NoHookEffect) {// 执行清理函数if (effect.destroy !== undefined) {effect.destroy();}// 执行 effect 创建函数effect.destroy = effect.create();}effect = effect.next;} while (effect !== firstEffect);}}
}
使用示例
function MyComponent() {// 模拟 useStateconst [count, setCount] = useState(0);// 模拟 useEffectuseEffect(() => {console.log(`Count changed to ${count}`);return () => {console.log(`Cleaning up effect for count ${count}`);};}, [count]);return {render: () => {console.log(`Rendering with count: ${count}`);// 模拟渲染输出return { count };},click: () => setCount(count + 1)};
}// 模拟渲染流程
let app = MyComponent();
let output = app.render(); // 首次渲染
console.log(output); // { count: 0 }app.click(); // 触发更新
output = app.render(); // 重新渲染
console.log(output); // { count: 1 }
实现要点说明
依赖项比较:通过比较新旧依赖数组决定是否执行 effect
清理机制:每次 effect 执行前会先执行上一次的清理函数
执行时机:在组件渲染后异步执行(模拟 React 的 commit 阶段)
Hook 规则:通过链表结构保证 Hook 的调用顺序稳定
这个实现简化了 React 的实际实现,但包含了 useEffect
的核心逻辑:依赖跟踪、清理机制和异步执行。React 实际的实现更加复杂,涉及 Fiber 架构、调度系统和优先级处理等。