From 9c28730be3ad5fbf66021b9355919d4db2c53a89 Mon Sep 17 00:00:00 2001 From: "JH.Lee" Date: Thu, 6 Mar 2025 16:59:57 +0900 Subject: [PATCH 1/2] feat(react): stabilize lazy load plugin --- .../src/{future => __internal__}/lazy.ts | 0 integrations/react/src/future/index.ts | 2 +- .../react/src/stable/LazyLoadPlugin.ts | 67 +++++++++++++++++++ integrations/react/src/stable/index.ts | 1 + integrations/react/src/stable/stackflow.tsx | 10 ++- 5 files changed, 76 insertions(+), 4 deletions(-) rename integrations/react/src/{future => __internal__}/lazy.ts (100%) create mode 100644 integrations/react/src/stable/LazyLoadPlugin.ts diff --git a/integrations/react/src/future/lazy.ts b/integrations/react/src/__internal__/lazy.ts similarity index 100% rename from integrations/react/src/future/lazy.ts rename to integrations/react/src/__internal__/lazy.ts diff --git a/integrations/react/src/future/index.ts b/integrations/react/src/future/index.ts index a05bfe92c..975956748 100644 --- a/integrations/react/src/future/index.ts +++ b/integrations/react/src/future/index.ts @@ -26,4 +26,4 @@ export * from "./useConfig"; /** * Utils */ -export * from "./lazy"; +export * from "../__internal__/lazy"; diff --git a/integrations/react/src/stable/LazyLoadPlugin.ts b/integrations/react/src/stable/LazyLoadPlugin.ts new file mode 100644 index 000000000..ff9787684 --- /dev/null +++ b/integrations/react/src/stable/LazyLoadPlugin.ts @@ -0,0 +1,67 @@ +import type { StackflowReactPlugin } from "../__internal__/StackflowReactPlugin"; +import type { BaseActivities } from "./BaseActivities"; +import type { StackflowOptions } from "./stackflow"; + +export function lazyLoadPlugin( + options: StackflowOptions, +): StackflowReactPlugin { + return () => ({ + key: "plugin-lazy-load", + onBeforePush: createBeforeRouteHandler(options), + onBeforeReplace: createBeforeRouteHandler(options), + }); +} + +type OnBeforeRoute = NonNullable< + | ReturnType["onBeforePush"] + | ReturnType["onBeforeReplace"] +>; + +function createBeforeRouteHandler( + options: StackflowOptions, +): OnBeforeRoute { + return ({ actionParams, actions: { pause, resume } }) => { + const { activityName } = actionParams; + + const matchActivityComponent = options.activities[activityName]; + + if (!matchActivityComponent) { + return; + } + + const lazyComponentPromise = + "_load" in matchActivityComponent + ? matchActivityComponent._load?.() + : undefined; + + if (!lazyComponentPromise) { + return; + } + + pause(); + + lazyComponentPromise + .catch((reason) => { + printLazyComponentPromiseError({ + reason, + activityName, + }); + }) + .finally(() => { + resume(); + }); + }; +} + +function printLazyComponentPromiseError({ + reason, + activityName, +}: { + reason: PromiseRejectedResult["reason"]; + activityName: string; +}) { + console.error(reason); + console.error( + `The above error occurred while loading a lazy react component of the "${activityName}" activity`, + ); +} diff --git a/integrations/react/src/stable/index.ts b/integrations/react/src/stable/index.ts index 4eaf37065..aa98c0ba9 100644 --- a/integrations/react/src/stable/index.ts +++ b/integrations/react/src/stable/index.ts @@ -9,3 +9,4 @@ export * from "./useActiveEffect"; export * from "./useEnterDoneEffect"; export * from "./useStep"; export * from "./useStepActions"; +export * from "../__internal__/lazy"; diff --git a/integrations/react/src/stable/stackflow.tsx b/integrations/react/src/stable/stackflow.tsx index 29878f6fb..b16081d8b 100644 --- a/integrations/react/src/stable/stackflow.tsx +++ b/integrations/react/src/stable/stackflow.tsx @@ -20,6 +20,7 @@ import { CoreProvider } from "../__internal__/core"; import { PluginsProvider } from "../__internal__/plugins"; import { isBrowser, makeRef } from "../__internal__/utils"; import type { BaseActivities } from "./BaseActivities"; +import { lazyLoadPlugin } from "./LazyLoadPlugin"; import type { UseActionsOutputType } from "./useActions"; import { useActions } from "./useActions"; import type { UseStepActionsOutputType } from "./useStepActions"; @@ -129,9 +130,12 @@ export type StackflowOutput = { export function stackflow( options: StackflowOptions, ): StackflowOutput { - const plugins = (options.plugins ?? []) - .flat(Number.POSITIVE_INFINITY as 0) - .map((p) => p as StackflowReactPlugin); + const plugins = [ + ...(options.plugins ?? []) + .flat(Number.POSITIVE_INFINITY as 0) + .map((p) => p as StackflowReactPlugin), + lazyLoadPlugin(options), + ]; const activityComponentMap = Object.entries(options.activities).reduce( (acc, [key, Activity]) => ({ From abde80eedbac366a7f689beb6450b05c7b9e9a10 Mon Sep 17 00:00:00 2001 From: "JH.Lee" Date: Thu, 6 Mar 2025 17:01:02 +0900 Subject: [PATCH 2/2] chore: add changeset --- .changeset/spotty-comics-build.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-comics-build.md diff --git a/.changeset/spotty-comics-build.md b/.changeset/spotty-comics-build.md new file mode 100644 index 000000000..a9a8e0abc --- /dev/null +++ b/.changeset/spotty-comics-build.md @@ -0,0 +1,5 @@ +--- +"@stackflow/react": minor +--- + +feat(react): stabilize lazy load plugin