Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve resolver performance #623

Merged
merged 10 commits into from
May 16, 2024
5 changes: 5 additions & 0 deletions .changeset/fluffy-days-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@craftjs/core': minor
---

Resolve performance issue within resolveComponent.
50 changes: 29 additions & 21 deletions packages/core/src/utils/resolveComponent.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
import { ERROR_NOT_IN_RESOLVER } from '@craftjs/utils';
import React from 'react';
import invariant from 'tiny-invariant';

import { Resolver } from '../interfaces';

let currResolver: Resolver | undefined = undefined;
const reversedResolver = new Map<React.ElementType | string, string>();

export const resolveComponent = (
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

type ReversedResolver = Map<React.ComponentType | string, string>;

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) {...}
}

const searchComponentInResolver = (
  resolver: Resolver,
  comp: React.ElementType | string
): string | null => {
  const name = getReversedResolver(resolver).get(comp);

   if (name !== null) {
     return name;
   }

  if (typeof comp === 'string') {
     return comp;
  }

  return null;
};

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;
}
};

const resolvedName = getNameInResolver();
const resolvedName = resolver[componentName]
? componentName
: searchComponentInResolver(resolver, comp);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Think we can just simplify this to searchComponentInResolver in all cases since it won't really impact efficiency after the initial call:

const resolvedName = searchComponentInResolver(resolver, comp);


invariant(
resolvedName,
Expand All @@ -37,3 +23,25 @@ export const resolveComponent = (

return resolvedName;
};

const searchComponentInResolver = (
resolver: Resolver,
comp: React.ElementType | string
): string | undefined => {
if (currResolver !== resolver) updateReversedResolver(resolver);

const name = reversedResolver.get(comp);
if (name !== undefined) return name;

if (typeof comp === 'string') return comp;

return undefined;
};

const updateReversedResolver = (resolver: Resolver): void => {
currResolver = resolver;
reversedResolver.clear();
for (const [name, comp] of Object.entries(resolver)) {
reversedResolver.set(comp, name);
}
};
Loading