Skip to content

Commit

Permalink
feat: use Map instead of plain object (#65)
Browse files Browse the repository at this point in the history
* feat: use Map instead of plain object

* fix: return isCached function

* fix: return some base API to avoid multiple changes

* fix: set cached true by default

* fix: move isCached to map

* feat: simplify logic for getComponentFromCache

* fix: import isCached from map instead of index file

* fix: do not export cache
  • Loading branch information
IvanIhnatsiuk authored Jul 12, 2024
1 parent aa3f4d8 commit 3edb9d8
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 28 deletions.
6 changes: 1 addition & 5 deletions src/bundler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import { Component } from './interface';

type Loadable = {
[key: string]: Component
}

export const mapLoadable: Loadable = {};
export const mapLoadable = new Map<string, Component>();
20 changes: 12 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as React from 'react';

import optimized from './optimized';
import { mapLoadable } from './bundler';
import { Component, EnhancedPreLoadable, PreLoadable } from './interface';
import { getComponent, isCached } from './map';
import { isCached, getComponent } from './map';

const defaultPreLoadable: EnhancedPreLoadable = {
cached: true,
Expand All @@ -19,23 +17,29 @@ const register = <P extends {}>(component: PreLoadable & Partial<EnhancedPreLoad
...defaultPreLoadable,
...component
};

const { name } = enhancedComponent;

if (mapLoadable[name] !== undefined) {
if (mapLoadable.get(name) !== undefined) {
console.warn(`You are trying to add a new component with already existing name: ${name}. If you see this warning after fast/hot reloading and you use 'register' function in Navigator without separate 'index' files - it's fine. In other cases it may indicate an issue, since by default it will overwrite the data and screen will not be cached. See https://github.com/kirillzyusko/react-native-bundle-splitter/issues/29 for more details.`);
}

mapLoadable[name] = enhancedComponent;
mapLoadable.set(name, enhancedComponent);

return optimized<P>(name);
};

const component = (name: string) => getComponent(name);

const group = (name: string) => {
const components = Object.keys(mapLoadable).filter((componentName) => mapLoadable[componentName].group === name);
const group = (groupName: string) => {
const components: ReturnType<(typeof getComponent)>[] = [];
mapLoadable.forEach(({ group, name }) => {
if (group === groupName) {
components.push(component(name))
}
});

return Promise.all(components.map((name) => component(name)))
return Promise.all(components)
};

const preloadAPI = {
Expand Down
12 changes: 8 additions & 4 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ export type RequireLoader = () => NodeRequire;

export type ImportLoader = () => Promise<ImportReturnType>;

type BasePreLoadable = {
require?: RequireLoader;
loader?: ImportLoader;
type BaseComponent = {
name?: string;
group?: string;
static?: object;
};

type BasePreLoadable = BaseComponent & {
require?: RequireLoader;
loader?: ImportLoader;
};

// helper, which transforms optional params to mandatory
// useful for creating conditional types - see usage below
// https://stackoverflow.com/a/49725198/9272042
Expand All @@ -35,4 +38,5 @@ export type EnhancedPreLoadable = {
extract: string,
};

export type Component = PreLoadable & EnhancedPreLoadable & { name: string };
export type Component = PreLoadable & EnhancedPreLoadable & { name: string };
export type CachedComponent = BaseComponent & { component: typeof React.Component };
16 changes: 8 additions & 8 deletions src/map.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { mapLoadable } from './bundler';
import { RequireLoader, ImportLoader } from './interface';
import { RequireLoader, ImportLoader, CachedComponent } from './interface';

const cache = {} as any;
const cache = new Map<string, CachedComponent>();

export const isCached = (componentName: string) => !!cache[componentName];
export const isCached = cache.has;

const DEPRECATED_API_MESSAGE = "You are using a deprecated API that will be removed in a future releases. Please consider using `loader` instead of `require`";
const ERROR_WHILE_LOADING = "An error occurred while lazy loading a component. Perhaps the path where you are trying to load the component does not exist? Stacktrace: ";
Expand All @@ -28,7 +28,7 @@ const nonBlockingLoader = (loader: RequireLoader | ImportLoader) => new Promise(

export const getComponent = async (name: string) => {
if (!isCached(name)) {
const { require: load, loader, ...rest } = mapLoadable[name];
const { require: load, loader, ...rest } = mapLoadable.get(name)!;
let component = null;

if (loader) {
Expand All @@ -39,14 +39,14 @@ export const getComponent = async (name: string) => {
component = await nonBlockingLoader(load);
}

cache[name] = {
cache.set(name, {
...rest,
// @ts-ignore
component: component[rest.extract],
};
});
}

return cache[name];
return cache.get(name);
};

export const getComponentFromCache = (name: string) => cache[name];
export const getComponentFromCache = cache.get;
6 changes: 3 additions & 3 deletions src/optimized.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Props<T> = T & {
};

const Suspender = React.forwardRef(function <T extends {}>({ screenName, ...rest }: Props<T>, ref: React.Ref<any>) {
const Component = isCached(screenName) ? getComponentFromCache(screenName).component : null;
const Component = isCached(screenName) ? getComponentFromCache(screenName)!.component : null;

if(!Component) {
throw getComponent(screenName);
Expand All @@ -19,10 +19,10 @@ const Suspender = React.forwardRef(function <T extends {}>({ screenName, ...rest

const optimized = <T extends object>(screenName: string): React.ForwardRefExoticComponent<React.PropsWithoutRef<T> & React.RefAttributes<unknown>> => {
return React.forwardRef((props: T, ref) => {
const { placeholder } = mapLoadable[screenName];
const { placeholder } = mapLoadable.get(screenName) || {};

return (
<React.Suspense fallback={placeholder}>
<React.Suspense fallback={placeholder || null}>
<Suspender ref={ref} screenName={screenName} {...props} />
</React.Suspense>
);
Expand Down

0 comments on commit 3edb9d8

Please sign in to comment.