wip added hooks and some basic debugging

This commit is contained in:
MasterGordon 2026-03-06 21:12:24 +01:00
parent 868305cc63
commit e12618a688
3 changed files with 93 additions and 18 deletions

View File

@ -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<T>(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];
};
}

View File

@ -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 <div>Child</div>;
};
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)}
<Child />
</div>
);
};

View File

@ -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 & {