在上面基础上 我们做了 react基础渲染和协调过程
在上面基础上 我们想实现 函数组件 和 hook相关
函数组件
src/index.ts
import React from './react';
import ReactDOM from './react-dom';
type AppProps = {
name: string;
}
/** @jsxRuntime classic */
function App(props: AppProps) {
return (
<div style={{background: 'salmon'}}>
<h1>Hi {props.name}</h1>
<h2 style={{textAlign: 'right' }}>from Didact</h2>
</div>
)
}
/** @jsxRuntime classic */
const element = <App name="world" />
const container = document.getElementById("root") as HTMLElement;
ReactDOM.render(element, container);
step1 改造fiber children 的获取创建
function performUnitOfWork(fiber: FiberProps): FiberProps | null | undefined {
// 是否是函数组建
const isFunctionComponent = fiber.type instanceof Function
if (isFunctionComponent) {
updateFunctionComponent(fiber)
} else {
updateHostComponent(fiber)
}
...
}
// 更新函数组建
function updateFunctionComponent(fiber: FiberProps) {
const children = [(fiber.type as Function)(fiber.props)]
reconcileChildren(fiber, children)
}
// 更新原始虚拟DOM
function updateHostComponent(fiber: FiberProps) {
if (!fiber.dom) {
fiber.dom = createDom(fiber)
}
// 生成fiber
const elements = fiber.props.children;
reconcileChildren(fiber, elements)
}
step2 commit阶段 改造 获取dom逻辑
function commitWork(fiber: FiberBlock) {
...
// const domParent = fiber.parent!.dom;
// 因为如果是 函数组建 parent 没办法直接获取到 要通过递归获取到父节点DOM
let domParentFiber = fiber.parent
while (!domParentFiber?.dom) {
domParentFiber = domParentFiber?.parent
}
const domParent = domParentFiber.dom
...
else if (fiber.effectTag === "DELETION") {
// 删除dom节点
commitDeletion(fiber, domParent)
// domParent!.removeChild(fiber.dom as Element)
}
}
function commitDeletion(fiber: FiberProps, domParent: Element) {
if (fiber.dom) {
domParent.removeChild(fiber.dom)
} else {
commitDeletion(fiber.child as FiberProps, domParent)
}
}
代码地址: github.com/beewolf233/…
hook useState
每个Fiber节点上 都有 hooks 节点 主要用在 type为 函数组件时
// 单个工作格类型
export type FiberProps = VDOMProps & {
...
/** hooks */
hooks?: {
state: any;
queue: Function[]
}[]
}
step1: 更新函数组建时,设置正在执行的wipFiber, 增加hook属性
// 正在执行的fiber
let wipFiber = null as FiberBlock;
let hookIndex = null as number | null;
function updateFunctionComponent(fiber: FiberProps) {
wipFiber = fiber
hookIndex = 0
wipFiber.hooks = []
const children = [(fiber.type as Function)(fiber.props)]
reconcileChildren(fiber, children)
}
step2: 执行useState, 为wipFiber传入 hook相关, 包含老的state
export function useState<T>(initial: T) {
// 检查是否有旧的hooks
const oldHook =
wipFiber!.alternate &&
wipFiber!.alternate.hooks &&
wipFiber!.alternate.hooks[hookIndex as number]
// 如果有旧的,就复制到新的,如果没有初始化
const hook = {
state: oldHook ? oldHook.state : initial,
queue: [],
}
// 是否有动作 有的话 就执行 (比如state+1 这个动作 )
const actions = oldHook ? oldHook.queue : []
actions.forEach(action => {
hook.state = action(hook.state)
})
const setState = (action: Function) => {
...
}
// @ts-ignore
wipFiber!.hooks.push(hook)
hookIndex = hookIndex as number + 1;
return [hook.state, setState]
}
step3 通过setState 重新 执行render 虚拟dom渲染相关
const setState = (action: Function) => {
// @ts-ignore
hook.queue.push(action)
wipRoot = {
dom: currentRoot!.dom,
props: currentRoot!.props,
alternate: currentRoot!,
} as FiberProps
nextUnitOfWork = wipRoot
deletions = []
}
以上就完成了基本的react
代码地址: github.com/beewolf233/…