Compare commits

..

2 Commits

Author SHA1 Message Date
MasterGordon e12618a688 wip added hooks and some basic debugging 2026-03-06 21:12:24 +01:00
MasterGordon 868305cc63 removed comments 2025-09-09 17:32:08 +02:00
3 changed files with 93 additions and 22 deletions

View File

@ -4,6 +4,7 @@ import {
EffectTag, EffectTag,
isFunctionFiber, isFunctionFiber,
isHostFiber, isHostFiber,
StateHook,
type Child, type Child,
type FiberNode, type FiberNode,
type FunctionFiber, type FunctionFiber,
@ -90,16 +91,44 @@ function createFiberFromElement(element: JSXElement): FiberNode {
effectTag: EffectTag.NoEffect, effectTag: EffectTag.NoEffect,
alternate: null, alternate: null,
nextEffect: 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) { function updateFunctionComponent(fiber: FunctionFiber) {
wipFiber = fiber; wipFiber = fiber;
hookIndex = 0; hookIndex = 0;
wipFiber.hooks = []; wipFiber.hooks = [];
wipFiber.effectTag = EffectTag.NoEffect; wipFiber.effectTag = EffectTag.NoEffect;
const children = [fiber.type(fiber.pendingProps)]; if (fiber.alternate && canBailout(fiber)) {
reconcileChildren(fiber, children); fiber.pendingChildren = fiber.alternate.pendingChildren;
} else {
fiber.pendingChildren = [fiber.type(fiber.pendingProps)];
}
reconcileChildren(fiber, fiber.pendingChildren);
} }
function updateHostComponent(fiber: HostFiber) { function updateHostComponent(fiber: HostFiber) {
@ -128,6 +157,26 @@ function pushEffect(fiber: FiberNode) {
wipRoot.lastEffect = fiber; 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( function reconcileChildren(
wipFiber: FiberNode | RootFiber, wipFiber: FiberNode | RootFiber,
children: Child[] = [], children: Child[] = [],
@ -150,21 +199,9 @@ function reconcileChildren(
const isSameType = oldFiber && element && element.type === oldFiber.type; const isSameType = oldFiber && element && element.type === oldFiber.type;
if (isSameType && oldFiber) { if (isSameType && oldFiber) {
newFiber = { newFiber = createAlternate(oldFiber);
$$typeof: FiberNodeSymbol, newFiber.key = element.key ?? null;
type: oldFiber.type, newFiber.pendingProps = element.props;
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,
};
pushEffect(newFiber); pushEffect(newFiber);
} }
@ -183,6 +220,8 @@ function reconcileChildren(
effectTag: EffectTag.Placement, effectTag: EffectTag.Placement,
alternate: null, alternate: null,
nextEffect: null, nextEffect: null,
actions: null,
pendingChildren: [],
}; };
pushEffect(newFiber); pushEffect(newFiber);
} }
@ -394,6 +433,8 @@ export function createRootFiber(root: HTMLElement): RootFiber {
isRoot: true, isRoot: true,
firstEffect: null, firstEffect: null,
lastEffect: null, lastEffect: null,
actions: null,
pendingChildren: [],
}; };
const rootFiber: RootFiber = { const rootFiber: RootFiber = {
$$typeof: FiberNodeSymbol, $$typeof: FiberNodeSymbol,
@ -412,6 +453,8 @@ export function createRootFiber(root: HTMLElement): RootFiber {
isRoot: true, isRoot: true,
firstEffect: null, firstEffect: null,
lastEffect: null, lastEffect: null,
actions: null,
pendingChildren: [],
}; };
alternate.alternate = rootFiber; alternate.alternate = rootFiber;
for (const event of events) { for (const event of events) {
@ -430,9 +473,22 @@ export function render(rootFiber: RootFiber, element: JSXElement) {
rootFiber.alternate!.pendingProps = { rootFiber.alternate!.pendingProps = {
children: [element], children: [element],
}; };
// const fiber = createFiberFromElement(element);
// fiber.parent = rootFiber;
// rootFiber.child = fiber;
// wipRoot.child = fiber;
unitOfWork = wipRoot; 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"; import { domFiber } from "./symbols";
let renderCount = 0; let renderCount = 0;
const Child = () => {
logFiber("Child");
return <div>Child</div>;
};
const App = () => { const App = () => {
console.log("render"); console.log("render");
const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@ -15,6 +21,7 @@ const App = () => {
}} }}
> >
Hello world {String(++renderCount)} Hello world {String(++renderCount)}
<Child />
</div> </div>
); );
}; };

View File

@ -42,11 +42,17 @@ export const EffectHook = Symbol.for("cr.effect-hook");
type Hook = type Hook =
| { | {
type: typeof StateHook; type: typeof StateHook;
value: any;
} }
| { | {
type: typeof EffectHook; type: typeof EffectHook;
}; };
type Action = {
execute: (fiber: FiberNode) => void;
nextAction: null | Action;
};
export interface FiberNode { export interface FiberNode {
$$typeof: typeof FiberNodeSymbol; $$typeof: typeof FiberNodeSymbol;
// Identity // Identity
@ -63,12 +69,14 @@ export interface FiberNode {
dom: Node | null; dom: Node | null;
hooks: Hook[]; hooks: Hook[];
pendingProps: any; pendingProps: any;
pendingChildren: Child[];
effectTag: EffectTag; effectTag: EffectTag;
// Used for double buffering // Used for double buffering
alternate: FiberNode | null; alternate: FiberNode | null;
nextEffect: FiberNode | null; nextEffect: FiberNode | null;
actions: Action | null;
} }
export type FunctionFiber = FiberNode & { export type FunctionFiber = FiberNode & {