当前位置: 首页 > news >正文

模拟实现 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 }

实现要点说明

  1. 依赖项比较:通过比较新旧依赖数组决定是否执行 effect

  2. 清理机制:每次 effect 执行前会先执行上一次的清理函数

  3. 执行时机:在组件渲染后异步执行(模拟 React 的 commit 阶段)

  4. Hook 规则:通过链表结构保证 Hook 的调用顺序稳定

这个实现简化了 React 的实际实现,但包含了 useEffect 的核心逻辑:依赖跟踪、清理机制和异步执行。React 实际的实现更加复杂,涉及 Fiber 架构、调度系统和优先级处理等。

http://www.dtcms.com/a/336663.html

相关文章:

  • 配置 NVIDIA RTX 5090 + sm_120 + flashattention,已跑通一个大模型 ~~
  • clion 如何调试 redis(在 mac 上)
  • AMBA-AXI and ACE协议详解(三)
  • 期望分位数回归模型
  • 利用pypy加速pyxlsbwriter生成xlsb文件
  • 五、redis入门 之 客户端连接redis
  • 日语学习-日语知识点小记-进阶-JLPT-N1阶段蓝宝书,共120语法(3):21-30语法
  • 雷卯针对香橙派Orange Pi Kunpeng Pro开发板防雷防静电方案
  • CloudBeaver:基于浏览器的DBeaver
  • 机器学习案例——对好评和差评进行预测
  • 当AI替我“搬砖”,我的价值是什么?
  • 21.AlexNet
  • 金山办公的服务端开发工程师-25届春招部分笔试题
  • C# Newtonsoft.Json 反序列化子类数据丢失问题
  • DBeaver连接MySQL 8报错连接丢失
  • HTTP协议-3-HTTP/2是如何维持长连接的?
  • JAVA后端开发——Token自动续期机制的必要性
  • 【Linux内核】Linux信号机制
  • 【Linux】五种IO模型
  • JVM学习笔记-----StringTable
  • react 错误边界
  • Python 内置模块 collections 常用工具
  • 【撸靶笔记】第二关:GET -Error based -Intiger based
  • Spring Framework :IoC 容器的原理与实践
  • CW32L011_电机驱动器开发板试用
  • 工作中使用到的时序指标异常检测算法 TRPS 【Temporal Residual Pattern Similarity】和 K-sigma 算法
  • 区块链:数字时代信任基石的构建与创新
  • 25年第十本【金钱心理学】
  • 1. Docker的介绍和安装
  • 洛谷 P2324 [SCOI2005] 骑士精神-提高+/省选-