From e12618a688905b7ae9394fb80625aa6a2d414028 Mon Sep 17 00:00:00 2001 From: MasterGordon Date: Fri, 6 Mar 2026 21:12:24 +0100 Subject: [PATCH] wip added hooks and some basic debugging --- examples/client-renderer/src/cr.ts | 94 ++++++++++++++++++++++----- examples/client-renderer/src/main.tsx | 9 ++- examples/client-renderer/src/types.ts | 8 +++ 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/examples/client-renderer/src/cr.ts b/examples/client-renderer/src/cr.ts index 3cc8dc3..4c59442 100644 --- a/examples/client-renderer/src/cr.ts +++ b/examples/client-renderer/src/cr.ts @@ -4,6 +4,7 @@ import { EffectTag, isFunctionFiber, isHostFiber, + StateHook, type Child, type FiberNode, type FunctionFiber, @@ -90,16 +91,44 @@ function createFiberFromElement(element: JSXElement): FiberNode { effectTag: EffectTag.NoEffect, alternate: null, nextEffect: null, + actions: null, + pendingChildren: [], }; } +function canBailout(fiber: FiberNode) { + const alternate = fiber.alternate; + if (!alternate) { + return false; + } + if (fiber.actions) { + return false; + } + const propKeys = Object.keys(fiber.pendingProps); + const pendingPropKeys = Object.keys(alternate.pendingProps); + if (propKeys.length !== pendingPropKeys.length) { + return false; + } + if ( + propKeys.some( + (key) => fiber.pendingProps[key] !== alternate.pendingProps[key], + ) + ) + return false; + return true; +} + function updateFunctionComponent(fiber: FunctionFiber) { wipFiber = fiber; hookIndex = 0; wipFiber.hooks = []; wipFiber.effectTag = EffectTag.NoEffect; - const children = [fiber.type(fiber.pendingProps)]; - reconcileChildren(fiber, children); + if (fiber.alternate && canBailout(fiber)) { + fiber.pendingChildren = fiber.alternate.pendingChildren; + } else { + fiber.pendingChildren = [fiber.type(fiber.pendingProps)]; + } + reconcileChildren(fiber, fiber.pendingChildren); } function updateHostComponent(fiber: HostFiber) { @@ -128,6 +157,26 @@ function pushEffect(fiber: FiberNode) { wipRoot.lastEffect = fiber; } +function createAlternate(oldFiber: FiberNode): FiberNode { + return { + $$typeof: FiberNodeSymbol, + type: oldFiber.type, + key: null, + child: null, + sibling: null, + parent: wipFiber, + index: 0, + dom: oldFiber.dom, + hooks: [], + pendingProps: oldFiber.pendingProps, + effectTag: EffectTag.Update, + alternate: oldFiber, + nextEffect: null, + actions: null, + pendingChildren: [], + }; +} + function reconcileChildren( wipFiber: FiberNode | RootFiber, children: Child[] = [], @@ -150,21 +199,9 @@ function reconcileChildren( const isSameType = oldFiber && element && element.type === oldFiber.type; if (isSameType && oldFiber) { - newFiber = { - $$typeof: FiberNodeSymbol, - type: oldFiber.type, - key: element.key ?? null, - child: null, - sibling: null, - parent: wipFiber, - index: 0, - dom: oldFiber.dom, - hooks: [], - pendingProps: element.props, - effectTag: EffectTag.Update, - alternate: oldFiber, - nextEffect: null, - }; + newFiber = createAlternate(oldFiber); + newFiber.key = element.key ?? null; + newFiber.pendingProps = element.props; pushEffect(newFiber); } @@ -183,6 +220,8 @@ function reconcileChildren( effectTag: EffectTag.Placement, alternate: null, nextEffect: null, + actions: null, + pendingChildren: [], }; pushEffect(newFiber); } @@ -394,6 +433,8 @@ export function createRootFiber(root: HTMLElement): RootFiber { isRoot: true, firstEffect: null, lastEffect: null, + actions: null, + pendingChildren: [], }; const rootFiber: RootFiber = { $$typeof: FiberNodeSymbol, @@ -412,6 +453,8 @@ export function createRootFiber(root: HTMLElement): RootFiber { isRoot: true, firstEffect: null, lastEffect: null, + actions: null, + pendingChildren: [], }; alternate.alternate = rootFiber; for (const event of events) { @@ -432,3 +475,20 @@ export function render(rootFiber: RootFiber, element: JSXElement) { }; unitOfWork = wipRoot; } + +export function logFiber(label: string) { + console.log(label, unitOfWork); +} + +export function useState(initialValue: T) { + if (!unitOfWork) + throw new Error("useState can only be used inside of a component!"); + const fiber = unitOfWork; + const alternate = fiber.alternate?.hooks[hookIndex]; + if (alternate && alternate.type !== StateHook) + throw new Error("Hook order changed smth."); + const value: T = alternate ? alternate.value : initialValue; + const setValue = (newValue: T) => { + fiber.hooks[hookIndex]; + }; +} diff --git a/examples/client-renderer/src/main.tsx b/examples/client-renderer/src/main.tsx index 6434902..6026028 100644 --- a/examples/client-renderer/src/main.tsx +++ b/examples/client-renderer/src/main.tsx @@ -1,7 +1,13 @@ -import { createRootFiber, render, scheduleRerender } from "./cr"; +import { createRootFiber, logFiber, render, scheduleRerender } from "./cr"; import { domFiber } from "./symbols"; let renderCount = 0; + +const Child = () => { + logFiber("Child"); + return
Child
; +}; + const App = () => { console.log("render"); const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -15,6 +21,7 @@ const App = () => { }} > Hello world {String(++renderCount)} + ); }; diff --git a/examples/client-renderer/src/types.ts b/examples/client-renderer/src/types.ts index 848aba4..c675da3 100644 --- a/examples/client-renderer/src/types.ts +++ b/examples/client-renderer/src/types.ts @@ -42,11 +42,17 @@ export const EffectHook = Symbol.for("cr.effect-hook"); type Hook = | { type: typeof StateHook; + value: any; } | { type: typeof EffectHook; }; +type Action = { + execute: (fiber: FiberNode) => void; + nextAction: null | Action; +}; + export interface FiberNode { $$typeof: typeof FiberNodeSymbol; // Identity @@ -63,12 +69,14 @@ export interface FiberNode { dom: Node | null; hooks: Hook[]; pendingProps: any; + pendingChildren: Child[]; effectTag: EffectTag; // Used for double buffering alternate: FiberNode | null; nextEffect: FiberNode | null; + actions: Action | null; } export type FunctionFiber = FiberNode & {