首页 > 其他分享 >一周-每天1 小时,学习实现一个简单的mini-React (三)G

一周-每天1 小时,学习实现一个简单的mini-React (三)G

时间:2024-04-28 19:35:05浏览次数:28  
标签:function mini fiber stateHook 一周 dom React state action

关于 从0到1实现一个 mini-React 过程

上周写了相对代码实现第一部分,本片文章接着写!

崔学社 mini-React

完整代码 whoelse666/mini-React

过程 ---

---------- day05 ----------

当日目标: 实现React.useState

对照React的API

//一个count++ 的例子
   const [count, setCount] = React.useState(1);
   setCount(c => c + 1)} //setCount参数是一个函数(也可以是一个值) 执行一次count + 1

我们可以得知useState 方法返回了一个包含一个值和一个函数的数组,那么首先第一步我们可以先去实现一个这样结构的函数

/*
//React.js
function useState(initial) { 
  const stateHook = {
    state: nitial,
  }; 
  
  function setState(fn){}
  return [stateHook.state, setState];
}

然后先对state 下手,我们可以先想想再使用React。useState的时候state,经历了什么,
1.先初始化一个值,
2.在setState的时候修改state,并且修改后的值在保存下来,在下次useState

那么和之前一样
我们可以利用fiber存储

/*
//React.js
function useState(initial) { 
//获取之前的fiber节点
  const oldHook = currentFiber.alternate?.stateHook


//判断是否有oldHook,区分初始化,还是更新
  const stateHook = {
     state: oldHook ? oldHook.state : initial,
  }; 
    currentFiber.stateHooks = stateHooks;
  function setState(action){
  stateHook.state = action(stateHook.state)
  
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    };
    nextWorkOfUnit = wipRoot;
  }
  return [stateHook.state, setState];
}

到这里呢就可以实现useState的跟个更新了;但是,如果同时存在多个useState ,上面的代码运行就会出现问题了,什么问题呢?就是所有state 共用了一个Hook,会相互影响。
多个问题,第一时间想到的那么是不是可以通过数组将多个hook存起来,然后再对应的hook执行呢。。。

/*
//React.js
let  stateHooks= [],stateHookIndex=0
function useState(initial) { 
//获取之前的fiber节点
  const oldHook = currentFiber.alternate?.stateHooks[stateHookIndex];


//判断是否有oldHook,区分初始化,还是更新
  const stateHook = {
     state: oldHook ? oldHook.state : initial,
  }; 
      stateHooks.push(stateHook);
    currentFiber.stateHooks = stateHooks;
     
  function setState(action){
  stateHook.state = action(stateHook.state)
  stateHookIndex++;
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    };
    nextWorkOfUnit = wipRoot;
  }
  return [stateHook.state, setState];
}

进一步优化: 我们都知道在React中,useState是异步的,而我们这里是同步的,就会出现重复执行,非必要的变化,导致性能浪费。 所以我们需要收集action,然后统一执行。

/*
//React.js
let  stateHooks= [],stateHookIndex=0
function useState(initial) { 
   let currentFiber = wipFiber;
  const oldHook = currentFiber.alternate?.stateHooks[stateHookIndex];
  const stateHook = {
    state: oldHook ? oldHook.state : initial,
    queue: oldHook ? oldHook.queue : [] //统一批量处理useState里的action
  };
  stateHook.queue.forEach(action => {
    stateHook.state = action(stateHook.state);
  });
   stateHook.queue = [];
   stateHookIndex++;
  stateHooks.push(stateHook);
  currentFiber.stateHooks = stateHooks;
  
  
  function setState(action){
      // 对不是function的在action 进行处理
    const eagerState = typeof action === 'function' ? action(stateHook.state) : action;
    if (eagerState === stateHook.state) {
      return;   
    }
    stateHook.queue.push(action);
  stateHook.state = action(stateHook.state)
  stateHookIndex++;
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    };
    nextWorkOfUnit = wipRoot;
  }
  return [stateHook.state, setState];
}

下面是完整代码

let wipRoot = null;
let currentRoot = null;
let nextWorkOfUnit = null;
let deletions = [];
let wipFiber = null;
let stateHooks = [],
 stateHookIndex = 0;
function createTextNode(text) {
 return {
   type: 'TEXT_ELEMENT',
   props: {
     nodeValue: text,
     children: []
   }
 };
}

function createElement(type, props, ...children) {
 return {
   type,
   props: {
     ...props,
     children: children.map(child => {
       const isTextNode = typeof child === 'string' || typeof child === 'number';
       return isTextNode ? createTextNode(child) : child;
     })
   }
 };
}

function render(el, container) {
 wipRoot = {
   dom: container,
   props: {
     children: [el]
   }
 };

 nextWorkOfUnit = wipRoot;
}

function workLoop(deadline) {
 let shouldYield = false;
 while (!shouldYield && nextWorkOfUnit) {
   nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit);
   if (wipRoot?.sibling?.type === nextWorkOfUnit?.type) {
     nextWorkOfUnit = undefined;
   }
   shouldYield = deadline.timeRemaining() < 1;
 }
 if (!nextWorkOfUnit && wipRoot) {
   commitRoot();
 }
 requestIdleCallback(workLoop);
}

function commitRoot() {
 deletions.forEach(commitDeletion);
 commitWork(wipRoot.child);

 currentRoot = wipRoot;
 wipRoot = null;
 deletions = [];
}


function commitDeletion(fiber) {
 if (fiber.dom) {
   let fiberParent = fiber.parent;
   while (!fiberParent.dom) {
     fiberParent = fiberParent.parent;
   }
   fiberParent.dom.removeChild(fiber.dom);
 } else {
   commitDeletion(fiber.child);
 }
}

function commitWork(fiber) {
 if (!fiber) return;

 let fiberParent = fiber.parent;
 while (!fiberParent.dom) {
   fiberParent = fiberParent.parent;
 }

 if (fiber.effectTag === 'update') {
   updateProps(fiber.dom, fiber.props, fiber.alternate?.props);
 } else if (fiber.effectTag === 'placement') {
   if (fiber.dom) {
     fiberParent.dom.append(fiber.dom);
   }
 }
 commitWork(fiber.child);
 commitWork(fiber.sibling);
}

function createDom(type) {
 return type === 'TEXT_ELEMENT' ? document.createTextNode('') : document.createElement(type);
}

function updateProps(dom, nextProps, prevProps) {
 // 1. old 有  new 没有 删除
 Object.keys(prevProps).forEach(key => {
   if (key !== 'children') {
     if (!(key in nextProps)) {
       dom.removeAttribute(key);
     }
   }
 });
 // 2. new 有 old 没有 添加
 // 3. new 有 old 有 修改
 Object.keys(nextProps).forEach(key => {
   if (key !== 'children') {
     if (nextProps[key] !== prevProps[key]) {
       if (key.startsWith('on')) {
         const eventType = key.slice(2).toLowerCase();

         dom.removeEventListener(eventType, prevProps[key]);

         dom.addEventListener(eventType, nextProps[key]);
       } else {
         dom[key] = nextProps[key];
       }
     }
   }
 });
}

function reconcileChildren(fiber, children) {
 let oldFiber = fiber.alternate?.child;
 let prevChild = null;
 children.forEach((child, index) => {
   const isSameType = oldFiber && oldFiber.type === child?.type;

   let newFiber;
   if (isSameType) {
     // update
     newFiber = {
       type: child.type,
       props: child.props,
       child: null,
       parent: fiber,
       sibling: null,
       dom: oldFiber.dom,
       effectTag: 'update',
       alternate: oldFiber
     };
   } else {
     if (child) {
       newFiber = {
         type: child.type,
         props: child.props,
         child: null,
         parent: fiber,
         sibling: null,
         dom: null,
         effectTag: 'placement'
       };
     }

     if (oldFiber) {
       deletions.push(oldFiber);
     }
   }

   if (oldFiber) {
     oldFiber = oldFiber.sibling;
   }

   if (index === 0) {
     fiber.child = newFiber;
   } else {
     prevChild.sibling = newFiber;
   }

   if (newFiber) {
     prevChild = newFiber;
   }
 });

 while (oldFiber) {
   deletions.push(oldFiber);

   oldFiber = oldFiber.sibling;
 }
}

function updateFunctionComponent(fiber) {
 stateHooks = [];
 stateHookIndex = 0;
 effectHooks = [];
 wipFiber = fiber;
 const children = [fiber.type(fiber.props)];
 reconcileChildren(fiber, children);
}

function updateHostComponent(fiber) {
 if (!fiber.dom) {
   const dom = (fiber.dom = createDom(fiber.type));

   updateProps(dom, fiber.props, {});
 }

 const children = fiber.props.children;
 reconcileChildren(fiber, children);
}

function performWorkOfUnit(fiber) {
 const isFunctionComponent = typeof fiber.type === 'function';
 if (isFunctionComponent) {
   updateFunctionComponent(fiber);
 } else {
   updateHostComponent(fiber);
 }

 // 4. 返回下一个要执行的任务
 if (fiber.child) {
   return fiber.child;
 }

 let nextFiber = fiber;
 while (nextFiber) {
   if (nextFiber.sibling) return nextFiber.sibling;
   nextFiber = nextFiber.parent;
 }
}

requestIdleCallback(workLoop);

function update() {
 let currentFiber = wipFiber;
 return () => {
   wipRoot = {
     ...currentFiber,
     alternate: currentFiber
   };
   nextWorkOfUnit = wipRoot;
 };
}

function useState(initial) {
 let currentFiber = wipFiber;
 const oldHook = currentFiber.alternate?.stateHooks[stateHookIndex];
 const stateHook = {
   state: oldHook ? oldHook.state : initial,
   queue: oldHook ? oldHook.queue : [] //统一批量处理useState里的action
 };
 stateHook.queue.forEach(action => {
   stateHook.state = action(stateHook.state);
 });
 stateHook.queue = [];
 stateHookIndex++;
 stateHooks.push(stateHook);
 currentFiber.stateHooks = stateHooks;
 function setState(action) {
   // 对不是function的在action 进行处理
   const eagerState = typeof action === 'function' ? action(stateHook.state) : action;
   if (eagerState === stateHook.state) {
     return;
   }
   stateHook.queue.push(action);
   wipRoot = {
     ...currentFiber,
     alternate: currentFiber
   };
   nextWorkOfUnit = wipRoot;
 }
 return [stateHook.state, setState];
}



const React = {
 update,
 render,
 useState,
 createElement
};

export default React;

Tips 刚开始写这类文章,经验不足,如有不足希望可以留言指正

标签:function,mini,fiber,stateHook,一周,dom,React,state,action
From: https://www.cnblogs.com/whoelse/p/18164361

相关文章

  • 使用minikube在云服务器安装k8s单机版
    1:前置安装了docker环境2:进入minikube的官网页面,下载命令https://minikube.sigs.k8s.io/docs/start/curl-LOhttps://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64sudoinstallminikube-linux-amd64/usr/local/bin/minikubeminikubekubectl--......
  • Winform程序使用app.minifest清单禁止高DPI无法失效问题
    问题:Winform程序使用app.minifest清单禁止高DPI无法失效问题摘要:因为笔记本基本都会有DPI放大,所以目前程序需要嵌入清单,并将其高DPI支持给禁止掉。环境搭建:Winform、app.minifest由于我的程序是使用CreateProcessAsUser来启动Winform,所以一开始以为是有权限问题。也有在群里跟......
  • vue和react有什么区别?
    (1)核心思想不同 vue:灵活易用的渐进式框架,进行数据拦截/代理,它对侦测数据的变化更敏感、更精确 react:React推崇函数式编程(纯组件),数据不可变以及单向数据流,当然需要双向的地方也可以手动实现,比如借助onChange和setState来实现(2)组件写法差异 vue:Vue推荐的做法是template......
  • SpringBoot集成minio前后端联调
    基本配置初始化项目新建一个SpringBoot项目,集成lombokmybatis-plusminiohutool-core(可有可无)。新建一个数据表attachement,用于存储文件上传后在minio中的位置。droptableifexistsattachment;createtableattachment(idintauto_increment......
  • ESP32-C3-MINI arduino测试
    1、文件——首选项2、设置开发板管理器网址3、工具——开发板——开发板管理器4、输入ESP32选择EspressifSystems最新版本、安装5、等待下载。。。6、等待安装完成7、选择开发板:ESP32C3DevModule8、新建例程9、修改LED_BUILTIN10、为IO标号数字11、连接最......
  • React 《useEffect》
    概念useEffect是一个ReactHook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用),比如发送AJAX请求,更改DOM等等:::warning说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”:::基础......
  • React 《组件间通信》
    React组件通信概念:组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信手段和方法A-B父子通信B-C兄弟通信A-E跨层通信父子通信-父传子基础实现实现步骤父组件传递数据-在子组件标签上绑定属性子组件接收数据-子组件通过props参数接收数据......
  • React 《入门案例》
    一、案例二、创建项目npminitvite@latest#选择react#删除不必要的css,文件等#安装依赖classnames、sass、uuid、dayjs、lodashnpmi-Sclassnames#处理className属性npmi-Suuid#生成uuidnpmi-Sdayjs#日期处理npmi-Slodash#操作数组npmi-D......
  • E. Chain Reaction
    https://codeforces.com/contest/1954/problem/E题意:n个数,可以对每个数释放闪电,闪电从释放的位置一直传到左右边界或者传到某个小于等于0的数终止,并且每个数都会减去闪电值k。问最少多少次释放闪电后可以让所有的数小于等于0。思路:从左往右考虑,假设第一个数的权值为1,如果当前数>......
  • MinIO 常用 API 快速入门
    快速入门minio中文网minio官网minio有开源版和收费版,使用开源版时,若修改了minio的源代码,需要将修改后的源代码完全公开。启动miniominio文档提供了多个运行环境的安装流程,此处以windows为例,其它运行环境文档上都有介绍。相关文档下载minio.exe:https://dl.minio......