From d8edfe8384c74dfb3dc64c804b00bb9211363a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rnar=20=C3=98sttveit?= <47412359+bjosttveit@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:44:07 +0100 Subject: [PATCH] Faster directChildren and pickDirectChildren (#2730) * speed up directChildren on layout page * more speed! * avoid unecessary readContext * improve pickDirectChildren * more correct? * fix setRowExtras optimization --- src/core/contexts/context.tsx | 8 ++--- src/utils/layout/LayoutPage.ts | 33 ++++++++++++++----- .../generator/NodeRepeatingChildren.tsx | 3 +- .../plugins/RepeatingChildrenPlugin.tsx | 23 +++++++------ .../plugins/RepeatingChildrenStorePlugin.tsx | 22 ++++++++++--- 5 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/core/contexts/context.tsx b/src/core/contexts/context.tsx index c5b5b1c97a..ffe56ac92e 100644 --- a/src/core/contexts/context.tsx +++ b/src/core/contexts/context.tsx @@ -50,12 +50,12 @@ export function createContext({ 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`); } diff --git a/src/utils/layout/LayoutPage.ts b/src/utils/layout/LayoutPage.ts index 14688e9e60..237f0585cb 100644 --- a/src/utils/layout/LayoutPage.ts +++ b/src/utils/layout/LayoutPage.ts @@ -12,19 +12,34 @@ export class LayoutPage implements LayoutObject { public layoutSet: LayoutPages; public pageKey: string; - private allChildren: Map = new Map(); + private allChildren: LayoutNode[] = []; + private allChildIds = new Set(); + + 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); + } } /** @@ -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 { @@ -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; } @@ -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 { @@ -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); } } diff --git a/src/utils/layout/generator/NodeRepeatingChildren.tsx b/src/utils/layout/generator/NodeRepeatingChildren.tsx index 8745b4b0dc..2068add44f 100644 --- a/src/utils/layout/generator/NodeRepeatingChildren.tsx +++ b/src/utils/layout/generator/NodeRepeatingChildren.tsx @@ -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, diff --git a/src/utils/layout/plugins/RepeatingChildrenPlugin.tsx b/src/utils/layout/plugins/RepeatingChildrenPlugin.tsx index 1c60ebe1da..3291ec61bf 100644 --- a/src/utils/layout/plugins/RepeatingChildrenPlugin.tsx +++ b/src/utils/layout/plugins/RepeatingChildrenPlugin.tsx @@ -218,24 +218,21 @@ export class RepeatingChildrenPlugin>, 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[]; + const rows = (state.item as any)[this.settings.internalProp] as (Row | 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; } @@ -255,3 +252,5 @@ export class RepeatingChildrenPlugin row && row.uuid !== undefined && row.itemIds !== undefined) ?? false; } } + +const emptyArray = []; diff --git a/src/utils/layout/plugins/RepeatingChildrenStorePlugin.tsx b/src/utils/layout/plugins/RepeatingChildrenStorePlugin.tsx index 75096f6ff7..97eafc431a 100644 --- a/src/utils/layout/plugins/RepeatingChildrenStorePlugin.tsx +++ b/src/utils/layout/plugins/RepeatingChildrenStorePlugin.tsx @@ -36,6 +36,10 @@ export class RepeatingChildrenStorePlugin extends NodeDataPlugin { 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'); @@ -43,22 +47,30 @@ export class RepeatingChildrenStorePlugin extends NodeDataPlugin