diff --git a/.changeset/fluffy-days-act.md b/.changeset/fluffy-days-act.md new file mode 100644 index 000000000..dd20f7036 --- /dev/null +++ b/.changeset/fluffy-days-act.md @@ -0,0 +1,5 @@ +--- +'@craftjs/core': minor +--- + +Improve resolver performance diff --git a/packages/core/src/utils/resolveComponent.ts b/packages/core/src/utils/resolveComponent.ts index 1d54c876f..edb9919e2 100644 --- a/packages/core/src/utils/resolveComponent.ts +++ b/packages/core/src/utils/resolveComponent.ts @@ -1,38 +1,60 @@ import { ERROR_NOT_IN_RESOLVER } from '@craftjs/utils'; +import React from 'react'; import invariant from 'tiny-invariant'; import { Resolver } from '../interfaces'; +type ReversedResolver = Map; + +type CachedResolverData = { + resolver: Resolver; + reversed: ReversedResolver; +}; + +let CACHED_RESOLVER_DATA: CachedResolverData | null = null; + +const getReversedResolver = (resolver: Resolver): ReversedResolver => { + if (CACHED_RESOLVER_DATA && CACHED_RESOLVER_DATA.resolver === resolver) { + return CACHED_RESOLVER_DATA.reversed; + } + + CACHED_RESOLVER_DATA = { + resolver, + reversed: new Map(), + }; + + for (const [name, comp] of Object.entries(resolver)) { + CACHED_RESOLVER_DATA.reversed.set(comp, name); + } + + return CACHED_RESOLVER_DATA.reversed; +}; + +const getComponentName = (component: React.ElementType): string | undefined => { + return (component as any).name || (component as any).displayName; +}; + +const searchComponentInResolver = ( + resolver: Resolver, + comp: React.ElementType +): string | null => { + const name = getReversedResolver(resolver).get(comp); + return name !== undefined ? name : null; +}; + export const resolveComponent = ( resolver: Resolver, comp: React.ElementType | string -) => { - const componentName = (comp as any).name || (comp as any).displayName; - - const getNameInResolver = () => { - if (resolver[componentName]) { - return componentName; - } - - for (let i = 0; i < Object.keys(resolver).length; i++) { - const name = Object.keys(resolver)[i]; - const fn = resolver[name]; - - if (fn === comp) { - return name; - } - } - - if (typeof comp === 'string') { - return comp; - } - }; +): string => { + if (typeof comp === 'string') { + return comp; + } - const resolvedName = getNameInResolver(); + const resolvedName = searchComponentInResolver(resolver, comp); invariant( resolvedName, - ERROR_NOT_IN_RESOLVER.replace('%node_type%', componentName) + ERROR_NOT_IN_RESOLVER.replace('%node_type%', getComponentName(comp)) ); return resolvedName;