React源码解析18(5)------ 实现函数组件【修改beginWork和completeWork】

摘要

经过之前的几篇文章,我们实现了基本的jsx,在页面渲染的过程。但是如果是通过函数组件写出来的组件,还是不能渲染到页面上的。

所以这一篇,主要是对之前写得方法进行修改,从而能够显示函数组件,所以现在我们在index.js文件中,修改一下jsx的写法。修改成函数组件:

javascript 复制代码
import jsx from '../src/react/jsx.js'
import ReactDOM from '../src/react-dom/index'

const root = document.querySelector('#root');

function App() {
  return jsx("div", {
    ref: "123",
    children: jsx("span", {
      children: "456"
    })
  });
}

ReactDOM.createRoot(root).render(<App />)

这里因为需要使用我们自己的jsx方法。所以在App里面返回的依旧是通过之前的方式进行调用。

1.修改reconcileChildren方法

我们来回忆一下,在beginWork阶段,我们主要是通过ReactElement,创建FilberNode。而reconcileChildren,就是创建FilberNode的方法。

在之前我们只处理了HostText类型和HostComponent类型,所以在这个方法里面,我们要对函数类型进行兼容,而作为函数组件的ReactElment,它最显而易见的特点就是type的值是一个函数。

例如上面的App组件,对应的ReactElement的type就是App。所以我们可以通过type来判断组件的类型:

javascript 复制代码
function reconcileChildren(element) {
  let tag;
  if(typeof element.type === 'function') {
    tag = FunctionComponent
  }
  //其他代码
  console.log(filberNode)
  return filberNode
}

我们打印一下看看,这个函数组件是否满足预期:

2.updateFunctionComponent方法

现在有了tag为FunctionComponent类型的FilberNode,在beginWork里面,我们就要对这个类型的FilberNode进行处理:

javascript 复制代码
function beginWork(nowFilberNode) {
  switch (nowFilberNode.tag) {
  	//其他代码
    case FunctionComponent: {
      return updateFunctionComponent(nowFilberNode)
    }
    //其他代码
  }
}

现在我们来实现updateFunctionComponent方法。

之前对于HostComponent类型的FilberNode,它的子节点其实就是它对应的ReactElement。

但是对于函数类型的FilberNode,我们想一下不就是它自己的返回值嘛?所以我们直接调用这个函数就能拿到它的子FilberNode了。

javascript 复制代码
function updateFunctionComponent(filberNode) {
  const nextChildren = filberNode.type();
  const newFilberNode = reconcileChildren(nextChildren);
  filberNode.child = newFilberNode;
  newFilberNode.return = filberNode;
  beginWork(newFilberNode)
}

2.修改completeWork方法

对于completeWork方法, 它的主要作用(目前)是给对应的FilberNode增加stateNode,而函数组件并没有自己对应的StateNode,所以直接继续递归就可以了:

javascript 复制代码
export const completeWork = (filberNode) => {
  const tag = filberNode.tag
  switch (tag) {
	//其他代码。。。
    case FunctionComponent: {
      completeWork(filberNode.child)
    }
  }
}

3.修改commitWork方法

对于之前的commitWork,我们是直接将最外层的FilberNode的stateNode挂载了容器上,现在由于最外层的可能是FunctionComponent,它是没有自己的stateNode的。所以我们要找到具有stateNode的最外层FilberNode。

javascript 复制代码
import { HostComponent } from "./filberNode";

export function commitWork(filberRootNode) {
  const container = filberRootNode.container;
  let node = filberRootNode.finishedWork;
  while( node.tag !== HostComponent ){
    node = node.child
  }
  container.appendChild(node.stateNode)
}

OK,经过上面的修改,我们的App组件也可以正常渲染了。

相关推荐
kyriewen115 小时前
你点的“刷新”是假刷新?前端路由的瞒天过海术
开发语言·前端·javascript·ecmascript·html5
Timer@6 小时前
LangChain 教程 04|Agent 详解:让 AI 学会“自己干活“
javascript·人工智能·langchain
阿珊和她的猫6 小时前
TypeScript中的never类型: 深入理解never类型的使用场景和特点
javascript·typescript·状态模式
九皇叔叔10 小时前
003-SpringSecurity-Demo 统一响应类
java·javascript·spring·springsecurity
低代码布道师12 小时前
纯代码实战:MBA培训管理系统 (十四) ——用户管理(批量选择与批量删除)
javascript·nextjs
Hello--_--World12 小时前
JavaScript运行机制、v8原理、js事件循环
开发语言·javascript·ecmascript
可问春风_ren14 小时前
HTML零基础进阶教程:解锁表单、多媒体与语义化实战
前端·git·html·ecmascript·reactjs·js
敲敲了个代码16 小时前
React 那么多状态管理库,到底选哪个?如果非要焊死一个呢?这篇文章解决你的选择困难症
前端·javascript·学习·react.js·前端框架
yungcy616316 小时前
React性能优化实战:从卡顿到丝滑,15个核心技巧覆盖全场景
前端·react.js·性能优化
阿珊和她的猫16 小时前
React 中 CSS 书写方式全解析
前端·css·react.js