2.基于Fiber架构实现一个简单的React-Hook实现

支持hook

第一个问题是我们要如何区分出class component和function commponent,react是通过在Component上定义一个isReactComponent方法来区分的。 大家可以看一下react作者Dan Abramov的overreacted.io/zh-hans/how... 这个方式来区分, 文章里面解决这个问题考虑到了许多的点 好多都是很难想到的

  • babel转义以后class会变成function

  • 存在多个react版本的问题 导致不能用 Child.prototype instanceof React.Component

js 复制代码
class Component {
    constructor(props) {
        this.props = props;
        this.state = this.state || {}
    }
    //这里
    isReactComponent() {
        return true;
    }

    setState(partialState) {
        //?Component的state是在哪里更新的
        //在resetNextUnitOfWork里面更新fiber节点上的partialState 然后在updateClassComponent把fiber上的partialState更新到Component上
        scheduleUpdate(this, partialState);
    } 
}

我们把这个判断的逻辑在reconcileChildArray加上去

js 复制代码
   if(element && !sameType) {
            /**
                这个情况stateNode没有创建,在下一次执行updateHostComponent or updateClassComponent的时候会创建
            */
            newFiber = {
                type: element.type,
                tag: typeof element.type === 'string'? HOST_COMPONENT: element.type.prototype.isReactComponent? ClASS_COMPONENT: FUNNCTION_COMPONENT,
                props: element.props,
                parent: wipFiber,
                effectTag: PLACEMENT 
            }
        }

ok现在我们实现一个方法来处理function类型的fiber节点

js 复制代码
function updateFunctionComponent(fiber) {
   wipFiber = fiber;
   hookIndex = 0;
   wipFiber.hooks = [];
   const newChildElements = wipFiber.type(wipFiber.props);
   reconcileChildArray(wipFiber, newChildElements);
}  

这里我们要把当前的fiber节点存到一个全局的变量wipFiber里面,这样hook里面才能访问到当前的fiber节点 hookIndex和wipFiber.hooks = []; 是什么作用?我们先定义一下useState这个hook,大家就知道了。

useState

因为useState是由用户来调用的,所以这里就不能把当前的fiber节点传到useState里面,只能把当前的fiber节点变成全局变量,useState里面才能访问

js 复制代码
function useState(initial) {    
    const oldHook = 
        wipFiber.alternate &&   
        wipFiber.alternate.hooks &&
        wipFiber.alternate.hooks[hookIndex]
    //如果没有调用当前setState,只是组件重新渲染调用useState, state里面还是上次渲染的值,不会变成初始值
    const hook = {
        state: oldHook?oldHook.state: initial,
        queue: []
    }

    //调用setState会先把action存到queue里面 然后在执行useState的时候执行queue
    const actions = oldHook? oldHook.queue: [];
    actions.forEach(action => {
        hook.state = action(hook.state);
    })

    const setState = value => {
        if(typeof value === "function") {
            hook.queue.push(value);
        } else {
            hook.state = value;
        }
        //class component 调用setState的时候可以通过class instance的__fiber属性找到当前的fiber节点 然后通过parent一直向上找到root fiber节点
        //hook没办法通过这种方式找到root fiber 只能通过把rootFiber变成全局变量的方式
        nextUnitOfWork = {
            tag: HOST_ROOT,
            stateNode: wipFiberRoot.stateNode,
            props: wipFiberRoot.props,
            alternate: wipFiberRoot
        }
        requestIdleCallback(performWork);
    }

    wipFiber.hooks.push(hook);
    hookIndex++;
    return [hook.state, setState];
}

调用setState的时候我们先把最新的值或者set function存到old fiber tree的hook上 然后开始重新构建fiber tree,重新构建fiber tree 就需要拿到old root fiber node,所以我们在前面声明了一个全局的wipFiberRoot用来存old root fiber node。

在重新构建fiber tree的时候我们会重新调用useState useState会从old fiber tree上就拿前面设置的值或者set function,然后计算出最新的state更新到最新的fiber tree。 可以看到useState是通过index来访问old fiber tree上面的对应的hook 所以必须保证每次渲染hooks的顺序不能变化,不然拿到的值就错乱了

dom更新阶段

只要在commitDeletion里面把FUNNCTION_COMPONENT的判断加上就可以了

js 复制代码
function commitDeletion(fiber, domParent) {
    let node = fiber;
    while (true) {
       //这里
      if (node.tag === ClASS_COMPONENT || node.tag === FUNNCTION_COMPONENT) {
        node = node.child;
        continue;
      }
      domParent.removeChild(node.stateNode);
      while (node != fiber && !node.sibling) {
        node = node.parent;
      }
      if (node == fiber) {
        return;
      }
      node = node.sibling;
    }
}
相关推荐
哑巴语天雨6 小时前
React+Vite项目框架
前端·react.js·前端框架
初遇你时动了情6 小时前
react 项目打包二级目 使用BrowserRouter 解决页面刷新404 找不到路由
前端·javascript·react.js
码农老起6 小时前
掌握 React:组件化开发与性能优化的实战指南
react.js·前端框架
前端没钱7 小时前
从 Vue 迈向 React:平滑过渡与关键注意点全解析
前端·vue.js·react.js
高山我梦口香糖10 小时前
[react] <NavLink>自带激活属性
前端·javascript·react.js
撸码到无法自拔10 小时前
React:组件、状态与事件处理的完整指南
前端·javascript·react.js·前端框架·ecmascript
高山我梦口香糖10 小时前
[react]不能将类型“string | undefined”分配给类型“To”。 不能将类型“undefined”分配给类型“To”
前端·javascript·react.js
乐闻x13 小时前
VSCode 插件开发实战(四):使用 React 实现自定义页面
ide·vscode·react.js
irisMoon0613 小时前
react项目框架了解
前端·javascript·react.js
web1508509664121 小时前
【React&前端】大屏适配解决方案&从框架结构到实现(超详细)(附代码)
前端·react.js·前端框架