当前位置: 首页 > 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.xdnf.cn/news/1318717.html

相关文章:

  • 【R语言】R 语言中打印含有双引号的字符串时会出现 “\” 的原因解析
  • 基于STM32单片机智能RFID刷卡汽车位锁桩设计
  • 基于51单片机汽车自动照明灯超声波光敏远近光灯设计
  • 自由学习记录(85)
  • TensorRT-LLM.V1.1.0rc0:在无 GitHub 访问权限的服务器上编译 TensorRT-LLM 的完整实践
  • 计算机网络 TCP time_wait 状态 详解
  • Java开发MCP服务器
  • thingsboard 服务器在2核CPU、2G内存资源配置下如何调优提速,适合开发/演示
  • vue封装请求拦截器 响应拦截器
  • 计算机网络 Session 劫持 原理和防御措施
  • 给纯小白的Python操作 PDF 笔记
  • 【算法】模拟专题
  • nertctl使用了解
  • B站 韩顺平 笔记 (Day 21)
  • Windows平台Frida逆向分析环境完整搭建指南
  • 机器学习05-朴素贝叶斯算法
  • 攻防世界—unseping(反序列化)
  • python的邮件发送及配置
  • 逆向Shell实战——红队技巧 vs 蓝队防御全攻略
  • Matlab数字信号处理——基于最小均方误差(MMSE)估计的自适应脉冲压缩算法复现
  • React 基础实战:从组件到案例全解析
  • Mysql笔记-错误条件\处理程序
  • 【Java后端】Spring Boot 集成 MyBatis 全攻略
  • 【前端基础】19、CSS的flex布局
  • 麒麟V10静默安装Oracle11g:lsnrctl、tnsping等文件大小为0的解决方案
  • 【编程实践】关于S3DIS数据集的问题
  • 官方正版在线安装office 365安装工具
  • react 错误边界
  • Linux系统分析 CPU 性能问题的工具汇总
  • STM32学习笔记13-通信协议I2CMPU6050