Skip to content

Commit

Permalink
Faster directChildren and pickDirectChildren (#2730)
Browse files Browse the repository at this point in the history
* speed up directChildren on layout page

* more speed!

* avoid unecessary readContext

* improve pickDirectChildren

* more correct?

* fix setRowExtras optimization
  • Loading branch information
bjosttveit authored Nov 20, 2024
1 parent c532f7d commit d8edfe8
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 32 deletions.
8 changes: 4 additions & 4 deletions src/core/contexts/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ export function createContext<T>({ name, required, ...rest }: CreateContextProps
});
Context.displayName = name;

const useHasProvider = () => Boolean(useContext(Context).provided);
const useHasProvider = () => useContext(Context).provided;

const useCtx = (): T => {
const hasProvider = useHasProvider();
const value = useContext(Context).innerValue;
if (!hasProvider) {
const ctx = useContext(Context);
const value = ctx.innerValue;
if (!ctx.provided) {
if (required) {
throw new Error(`${name} is missing`);
}
Expand Down
33 changes: 24 additions & 9 deletions src/utils/layout/LayoutPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,34 @@ export class LayoutPage implements LayoutObject {
public layoutSet: LayoutPages;
public pageKey: string;

private allChildren: Map<string, LayoutNode> = new Map();
private allChildren: LayoutNode[] = [];
private allChildIds = new Set<string>();

private _directChildren: LayoutNode[] = [];

/**
* Adds a child to the collection. For internal use only.
*/
public _addChild(child: LayoutNode) {
this.allChildren.set(child.id, child);
this.layoutSet.registerNode(child);
if (!this.allChildIds.has(child.id)) {
this.layoutSet.registerNode(child);
this.allChildIds.add(child.id);
this.allChildren.push(child);

// Direct children of a layout page are always static.
// Only children of components like repeating groups are dynamic
if (child.parent === this) {
this._directChildren.push(child);
}
}
}

public _removeChild(child: LayoutNode) {
this.allChildren.delete(child.id);
this.layoutSet.unregisterNode(child);
if (this.allChildIds.has(child.id)) {
this.layoutSet.unregisterNode(child);
this.allChildIds.delete(child.id);
this.allChildren.splice(this.allChildren.indexOf(child), 1);
}
}

/**
Expand All @@ -47,7 +62,7 @@ export class LayoutPage implements LayoutObject {
}

protected directChildren(_task: TraversalTask): LayoutNode[] {
return [...this.allChildren.values()].filter((node) => node.parent === this);
return this._directChildren;
}

public firstChild(task: TraversalTask): LayoutNode | undefined {
Expand All @@ -61,7 +76,7 @@ export class LayoutPage implements LayoutObject {
}

private firstDeepChild(task: TraversalTask): LayoutNode | undefined {
for (const node of this.allChildren.values()) {
for (const node of this.allChildren) {
if (task.passes(node)) {
return node;
}
Expand All @@ -86,7 +101,7 @@ export class LayoutPage implements LayoutObject {
}

public flat(task?: TraversalTask): LayoutNode[] {
return task ? [...this.allChildren.values()].filter((n) => task.passes(n)) : [...this.allChildren.values()];
return task ? this.allChildren.filter((n) => task.passes(n)) : this.allChildren;
}

public isRegisteredInCollection(layoutSet: LayoutPages): boolean {
Expand All @@ -98,7 +113,7 @@ export class LayoutPage implements LayoutObject {
this.layoutSet = layoutSet;
layoutSet.replacePage(this);

for (const node of this.allChildren.values()) {
for (const node of this.allChildren) {
layoutSet.registerNode(node);
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/utils/layout/generator/NodeRepeatingChildren.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ interface ResolveRowProps {
function ResolveRowExpressions({ plugin }: ResolveRowProps) {
const node = GeneratorInternal.useParent() as LayoutNode;
const rowIndex = GeneratorInternal.useRowIndex()!;
const nodeChildren = useNodeDirectChildren(node, rowIndex);
const firstChild = nodeChildren ? nodeChildren[0] : undefined;
const firstChild = useNodeDirectChildren(node, rowIndex).at(0);

const internal = NodesInternal.useNodeData(
node,
Expand Down
23 changes: 11 additions & 12 deletions src/utils/layout/plugins/RepeatingChildrenPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,24 +218,21 @@ export class RepeatingChildrenPlugin<E extends ExternalConfig = typeof defaultCo
}

pickDirectChildren(state: DefPluginState<ToInternal<E>>, restriction?: TraversalRestriction): string[] {
const out: string[] = [];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const rows = (state.item as any)[this.settings.internalProp] as Row<E>[];
const rows = (state.item as any)[this.settings.internalProp] as (Row<E> | undefined)[];
if (!rows) {
return out;
return emptyArray;
}

for (const row of rows) {
if (!row || (restriction !== undefined && row.index !== restriction)) {
continue;
}

for (const child of row.itemIds || []) {
child && out.push(child);
}
if (restriction !== undefined) {
return rows[restriction]?.itemIds ?? emptyArray;
}

const out: string[] = [];
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
row && out.push(...row.itemIds);
}
return out;
}

Expand All @@ -255,3 +252,5 @@ export class RepeatingChildrenPlugin<E extends ExternalConfig = typeof defaultCo
return rows?.every((row) => row && row.uuid !== undefined && row.itemIds !== undefined) ?? false;
}
}

const emptyArray = [];
22 changes: 17 additions & 5 deletions src/utils/layout/plugins/RepeatingChildrenStorePlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,41 @@ export class RepeatingChildrenStorePlugin extends NodeDataPlugin<RepeatingChildr
set((state) => {
let changes = false;
const nodeData = { ...state.nodeData };
const newPartialItems: {
[nodeId: string]: { [internalProp: string]: (RepChildrenRow | undefined)[] | undefined } | undefined;
} = {};

for (const { node, rowIndex, plugin, extras } of requests) {
if (typeof extras !== 'object' || !extras) {
throw new Error('Extras must be an object');
}

const internalProp = plugin.settings.internalProp;
const data = nodeData[node.id];
const existingRows = data && data.item && (data.item[internalProp] as RepChildrenRow[] | undefined);

const existingRows =
newPartialItems[node.id]?.[internalProp] ??
(data?.item?.[internalProp] as (RepChildrenRow | undefined)[] | undefined);
if (!existingRows) {
continue;
}

const existingRow = existingRows ? existingRows[rowIndex] : {};
const existingRow = existingRows[rowIndex] ?? ({} as RepChildrenRow);
const nextRow = { ...existingRow, ...extras, index: rowIndex } as RepChildrenRow;
if (deepEqual(existingRow, nextRow)) {
continue;
}

changes = true;
const newRows = [...(existingRows || [])];
newRows[rowIndex] = nextRow;
newPartialItems[node.id] ??= {};
newPartialItems[node.id]![internalProp] ??= [...(existingRows || [])];
newPartialItems[node.id]![internalProp]![rowIndex] = nextRow;
}

for (const [nodeId, partialItem] of Object.entries(newPartialItems)) {
const data = nodeData[nodeId];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
nodeData[node.id] = { ...data, item: { ...data.item, [internalProp]: newRows } as any };
nodeData[nodeId] = { ...data, item: { ...data.item, ...partialItem } as any };
}

return changes
Expand Down

0 comments on commit d8edfe8

Please sign in to comment.