Skip to content

Commit

Permalink
Store improvement (#13)
Browse files Browse the repository at this point in the history
* rf

* progress

* Update api.ts

* v1.3.2
  • Loading branch information
abhishiv authored Oct 13, 2024
1 parent 7114f04 commit 4d8029f
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 73 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "alfama",
"version": "1.3.1",
"version": "1.3.2",
"author": "Abhishiv Saxena<[email protected]>",
"license": "MIT",
"description": "Fine-grained reactive library with no compiler, no magic, and no virtual DOM",
Expand Down
15 changes: 2 additions & 13 deletions src/addons/animation/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import {
animate,
Animation,
AnimationOptions,
linear,
easeInOut,
} from "popmotion";
import { ComponentUtils } from "../../dom";

export const createAnimation = <T>(
utils: ComponentUtils,
initialValue: any,
options: AnimationOptions<T>
initialValue: any
) => {
const $signal = utils.signal("animation", initialValue);
const animation = animate({
...options,
});
return [$signal, animation];
return [$signal];
};
1 change: 1 addition & 0 deletions src/core/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./wire";
export * from "./store";
export * from "./storeAPI";
export * from "./utils";
export { dfsPreOrder } from "../../utils";
10 changes: 7 additions & 3 deletions src/core/state/storeAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,13 @@ export const reify = <T = unknown>(cursor: T): T => {
const manager: StoreManager = getCursorProxyMeta<StoreManager>(
s as unknown as ObjPathProxy<unknown, unknown>
);
const cursorPath = getCursor(s);
const v = getValueUsingPath(manager.value as any, cursorPath);
return v as T;
if (manager) {
const cursorPath = getCursor(s);
const v = getValueUsingPath(manager.value as any, cursorPath);
return v as T;
} else {
return cursor;
}
};

export const produce = <T = unknown>(
Expand Down
8 changes: 7 additions & 1 deletion src/core/state/wire.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,15 @@ export const runWire = (
}
};

const wireReset = (wire: Wire<any>): void => {
export const wireReset = (wire: Wire<any>): void => {
wire.lower.forEach(wireReset);
wire.sigRS.forEach((signal) => signal.wires.delete(wire));
wire.storesRS.forEach((store) => {
const manager = getCursorProxyMeta<StoreManager>(store);
if (manager) {
manager.wires.delete(wire);
}
});
_initWire(wire);
};

Expand Down
49 changes: 15 additions & 34 deletions src/dom/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
} from "../core/state";
import { LiveDocumentFragment } from "./dom";
import { getCursorProxyMeta } from "../utils";
import { unmount, unrender } from ".";

export const insertElement = (
renderContext: RenderContext,
Expand Down Expand Up @@ -165,6 +166,7 @@ export const addNode = (
export const rmNodes = (node: Node | LiveDocumentFragment) => {
if (node instanceof LiveDocumentFragment) {
const childNodes = getLiveFragmentChildNodes(node);
//console.log("childNodes", childNodes.length, childNodes);
childNodes.forEach((c) => c.parentNode?.removeChild(c));
} else {
node.parentElement?.removeChild(node);
Expand All @@ -175,45 +177,17 @@ export const removeNode = (renderCtx: RenderContext, node: TreeStep) => {
//console.log("removeNodes", nodes);
const nodes = getDescendants(node);
// console.log("removeNode nodes", node, nodes);
unrender(nodes);
nodes.forEach((step) => {
if (step.type === DOMConstants.ComponentTreeStep) {
//step.wires.length && console.log("s", step, step.wires.length);
step.wires.forEach((w) => {
w.storesRS.forEach((s, manager) => {
if (manager.wires.has(w)) {
//console.log("removing wire", s, manager);
manager.wires.delete(w);
}
});
w.sigRS.forEach((sig) => {
sig.wires.delete(w);
});
w.tasks.clear();
});
step.wires = [];
}
if (step.dom) {
if (
step.type === DOMConstants.ComponentTreeStep &&
step.onUnmount.length > 0
) {
step.onUnmount.forEach((el) => el());
for (var s in step.state.stores) {
// todo unsubscribe from store
}
}
rmNodes(step.dom);
step.dom = undefined;
}
renderCtx.reg.delete(step);
step.parent ? arrayRemove(step.parent.children, step) : null;
});
};

export const renderTreeStep = (renderCtx: RenderContext, element: VElement) => {
// todo: move this to getRenderContext so it clears DOM properly
//if (window.ss) return;
renderCtx.el.innerHTML = "";

const { root, registry } = reifyTree(renderCtx, element);
const id = getVirtualElementId(root.node);
if (!id) throw createError(101);
Expand All @@ -238,18 +212,20 @@ export const getRenderContext = (
emitter: new EEmitter(),
} as RenderContext);

//console.log("renderContext", renderContext);
renderContext.prevState.clear();

// so HMR is properly cleaned up
renderContext.reg.forEach((step) => {
//console.log("step", step);
if (
step.type === DOMConstants.ComponentTreeStep &&
step.onUnmount.length > 0
)
step.onUnmount.forEach((el) => el(step));
if (step.type === DOMConstants.ComponentTreeStep) {
if (step.mount && step.dom instanceof Element) {
step.dom.remove();
if (step.mount && step.dom) {
rmNodes(step.dom);
}
}

Expand All @@ -263,12 +239,17 @@ export const getRenderContext = (
}
ancestor = ancestor.parent;
}
renderContext.prevState.set(ids, step.state);
// dont preserve stores for now
renderContext.prevState.set(ids, {
...step.state,
stores: {},
signals: {},
});
}
});
//if (window.ss) return;

renderContext.reg.clear();

(container as any)[id] = renderContext;

return renderContext;
Expand Down
40 changes: 38 additions & 2 deletions src/dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
Component,
ComponentVElement,
NativeVElement,
RenderContext,
TreeStep,
} from "./types";
import { getRenderContext, renderTreeStep } from "./api";
import { getRenderContext, renderTreeStep, rmNodes } from "./api";
import { reifyTree } from "./traverser";
import type {
GenericEventAttrs,
Expand All @@ -17,7 +19,8 @@ import type {
SVGElements,
TargetedEvent,
} from "./jsx";
import { Wire } from "../core/state";
import { runWire, StoreManager, Wire, wireReset } from "../core/state";
import { getCursorProxyMeta } from "../utils";

export * from "./types";
export * as DOMConstants from "./constants";
Expand Down Expand Up @@ -74,6 +77,39 @@ export function render(
return renderContext;
}

export const unmount = (step: TreeStep) => {
if (step.dom) rmNodes(step.dom);
};

export function unrender(arg: RenderContext | TreeStep[]) {
const steps = Array.isArray(arg) ? arg : Array.from(arg.reg);
steps.forEach((step) => {
unmount(step);
if (step.type == DOMConstants.ComponentTreeStep) {
step.wires.forEach((w) => {
wireReset(w);
});
step.wires = [];
Object.values(step.state.stores).forEach((s) => {
const manager = getCursorProxyMeta<StoreManager>(s as any);
manager.tasks.clear();
manager.wires.clear();
// manager.unsubscribe();
});
step.onUnmount.forEach((el) => el(step));
// step.state.stores = {};
Object.values(step.state.signals).forEach((sig) => {
sig.wires.clear();
});
step.state.ctx.clear();
} else if (step.type == DOMConstants.WireTreeStep) {
const wire = step.node as Wire;
wireReset(wire);
}
});
return arg;
}

// used to store parent wire in context
export const ParentWireContext = defineContext("ParentWireContext");

Expand Down
2 changes: 1 addition & 1 deletion src/dom/traverser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const getTreeStep = (
return {
type: DOMConstants.ComponentTreeStep,
...step,
state: { signals: {}, ctx: new WeakMap(), stores: {} },
state: { signals: {}, ctx: new Map(), stores: {} },
wires: [],
onMount: [],
onUnmount: [],
Expand Down
15 changes: 6 additions & 9 deletions src/dom/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Signal, Wire, StoreCursor, WireFunction } from "../core/state";

import * as DOMConstants from "./constants";
export type DOMNode = HTMLElement | DocumentFragment;
export type DOMNodeType = HTMLElement | DocumentFragment;

export type PrimitiveType = string | number | boolean | null | undefined;

Expand Down Expand Up @@ -68,7 +68,7 @@ export type VElement<T = any> =

// Tree steps
export type BaseTreeStep = {
dom?: DOMNode;
dom?: DOMNodeType;
id?: string;
parent?: TreeStep;
meta?: Record<string, any>;
Expand All @@ -89,14 +89,11 @@ export interface ComponentTreeStep extends BaseTreeStep {
onUnmount: Function[];
onMount: Function[];
}

export interface ComponentTreeStepState {
signals: {
[name: string]: Signal;
};
stores: {
[name: string]: StoreCursor;
};
ctx: WeakMap<any, any>;
signals: Record<string, Signal>;
stores: Record<string, StoreCursor>;
ctx: Map<any, any>;
}

export interface PrimitiveTreeStep extends BaseTreeStep {
Expand Down
5 changes: 5 additions & 0 deletions src/stdlib/Each/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ export const Each: <T extends ArrayOrObject>(
if (!isArray) throw new Error("<Each/> needs array");

const getItemCursor = (item: ExtractElement<typeof listCursor>) => {
const store: StoreManager = (listCursor as any)[META_FLAG];
const listValue: typeof listCursor = getValueUsingPath(
store.value as any,
listCursorPath
) as typeof listCursor;
// console.log("listValue", listValue, item);
const index = listValue.indexOf(item);
if (index > -1) {
return props.cursor[index];
} else {
// debugger;
console.error("accessing no existent item", index, item, listValue);
}
};
Expand Down Expand Up @@ -107,6 +110,7 @@ export const Each: <T extends ArrayOrObject>(
if (path.slice(0, listCursorPath.length).join("/") !== path.join("/"))
return;
if (data?.name === "push") {
// console.log("data", data);
data.args.forEach((arg, i) => {
const index = previousChildren.length + i;
const { treeStep, el } = renderArray(
Expand All @@ -118,6 +122,7 @@ export const Each: <T extends ArrayOrObject>(
utils,
getItemCursor
);
// console.log({ treeStep, el, index, previousChildren });
const { registry, root } = reifyTree(renderContext, el, pStep);
addNode(renderContext, pStep, root);
});
Expand Down
5 changes: 3 additions & 2 deletions src/stdlib/Portal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/** @jsx h **/

import { h, component } from "../../dom";
import { rmNodes } from "../../dom/api";
import { ComponentTreeStep, VElement } from "../../dom/types";

export type PortalProps = {
mount: HTMLElement;
mount?: HTMLElement;
children: VElement;
};

Expand All @@ -24,7 +25,7 @@ export const Portal = component<PortalProps>(
}
) => {
onUnmount((step: any) => {
if (step && step.dom && step.dom.remove) step.dom.remove();
if (step && step.dom) rmNodes(step.dom);
});
(parentStep as ComponentTreeStep).mount = props.mount;
return props.children;
Expand Down
19 changes: 12 additions & 7 deletions src/stdlib/When/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { ParentWireContext } from "../../dom/index";
import { addNode, removeNode } from "../../dom/api";
import { reifyTree } from "../../dom/traverser";

export type WhenViews = { [key: string]: () => VElement };
export type WhenViews =
| Record<string, () => VElement>
| ((value: any) => VElement);
export type WhenProps = {
condition: ($: SubToken) => keyof WhenViews | boolean;
condition: ($: SubToken) => any;
views: WhenViews;
fallback?: () => VElement;
options?: { cached?: boolean };
Expand All @@ -31,13 +33,16 @@ export const When = component<WhenProps>(
renderContext,
}
) => {
// todo: important memory leak
const rootWire = wire(($: SubToken) => {});
setContext(ParentWireContext, signal("$wire", rootWire));
const underlying = utils.wire(props.condition);
const value = underlying.run();
const getView = (value: any) =>
props.views[value as unknown as any] || props.fallback;
const getView = (value: any) => {
//console.log("p", value, props.views, typeof props.views);
if (typeof props.views === "function") {
return () => (props.views as Function)(value);
} else {
return props.views[value as unknown as any] || props.fallback;
}
};
const task = (value: any) => {
const view = getView(value);
const u = view ? view() : null;
Expand Down

0 comments on commit 4d8029f

Please sign in to comment.