Skip to content

Commit 4ff2996

Browse files
riopopCopilot
andauthored
feat: add SuspenseWrappers to Runtime (#7131)
* feat: add SuspenseWrappers to Runtime * chore(runtime): format code * fix(runtime): Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * fix(runtime): remove redundant composeSuspenseWrappers function --------- Co-authored-by: Copilot <[email protected]>
1 parent ace5e1d commit 4ff2996

File tree

4 files changed

+67
-11
lines changed

4 files changed

+67
-11
lines changed

.changeset/ten-paws-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ice/runtime': patch
3+
---
4+
5+
feat: add SuspenseWrappers to Runtime

packages/runtime/src/Suspense.tsx

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ export function withSuspense(Component) {
135135
return (props: SuspenseProps) => {
136136
const { fallback, id, ...componentProps } = props;
137137

138-
const [suspenseState, updateSuspenseData] = React.useState({
138+
139+
const [suspenseState, updateSuspenseData] = React.useState<SuspenseState>({
139140
id: id,
140141
data: null,
141142
done: false,
@@ -156,24 +157,47 @@ export function withSuspense(Component) {
156157
updateSuspenseData(newState);
157158
}
158159

159-
return (
160-
<React.Suspense fallback={fallback || null}>
160+
161+
// Get SuspenseWrappers from app context
162+
const { SuspenseWrappers = [] } = useAppContext();
163+
164+
// Compose SuspenseWrappers
165+
const composeSuspenseWrappers = React.useCallback(
166+
(children: React.ReactNode) => {
167+
if (!SuspenseWrappers.length) return children;
168+
169+
return SuspenseWrappers.reduce((WrappedComponent, wrapperConfig) => {
170+
const { Wrapper } = wrapperConfig;
171+
return <Wrapper id={id}>{WrappedComponent}</Wrapper>;
172+
}, children);
173+
},
174+
[SuspenseWrappers, id],
175+
);
176+
177+
const wrappedComponent = (
178+
<>
161179
<InlineScript
162180
id={`suspense-parse-start-${id}`}
163181
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-start','${id}');`}
164182
/>
165-
<SuspenseContext.Provider value={suspenseState}>
166-
<Component {...componentProps} />
167-
<InlineScript
168-
id={`suspense-parse-data-${id}`}
169-
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-data','${id}');`}
170-
/>
171-
<Data id={id} />
172-
</SuspenseContext.Provider>
183+
<Component {...componentProps} />
184+
<InlineScript
185+
id={`suspense-parse-data-${id}`}
186+
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-data','${id}');`}
187+
/>
188+
<Data id={id} />
173189
<InlineScript
174190
id={`suspense-parse-end-${id}`}
175191
script={`(${DISPATCH_SUSPENSE_EVENT_STRING})('ice-suspense-parse-end','${id}');`}
176192
/>
193+
</>
194+
);
195+
196+
return (
197+
<React.Suspense fallback={fallback || null}>
198+
<SuspenseContext.Provider value={suspenseState}>
199+
{composeSuspenseWrappers(wrappedComponent)}
200+
</SuspenseContext.Provider>
177201
</React.Suspense>
178202
);
179203
};

packages/runtime/src/runtime.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import type {
1212
SetAppRouter,
1313
AddProvider,
1414
AddWrapper,
15+
AddSuspenseWrapper,
1516
RouteWrapperConfig,
17+
SuspenseWrapperConfig,
1618
SetRender,
1719
AppRouterProps,
1820
ComponentWithChildren,
@@ -33,6 +35,8 @@ class Runtime {
3335

3436
private RouteWrappers: RouteWrapperConfig[];
3537

38+
private SuspenseWrappers: SuspenseWrapperConfig[];
39+
3640
private render: Renderer;
3741

3842
private responseHandlers: ResponseHandler[];
@@ -46,6 +50,7 @@ class Runtime {
4650
return root;
4751
};
4852
this.RouteWrappers = [];
53+
this.SuspenseWrappers = [];
4954
this.runtimeOptions = runtimeOptions;
5055
this.responseHandlers = [];
5156
this.getAppRouter = this.getAppRouter.bind(this);
@@ -55,6 +60,7 @@ class Runtime {
5560
return {
5661
...this.appContext,
5762
RouteWrappers: this.RouteWrappers,
63+
SuspenseWrappers: this.SuspenseWrappers,
5864
};
5965
};
6066

@@ -72,6 +78,8 @@ class Runtime {
7278

7379
public getWrappers = () => this.RouteWrappers;
7480

81+
public getSuspenseWrappers = () => this.SuspenseWrappers;
82+
7583
public loadModule(module: RuntimePlugin | StaticRuntimePlugin | CommonJsRuntime) {
7684
let runtimeAPI: RuntimeAPI = {
7785
addProvider: this.addProvider,
@@ -80,6 +88,7 @@ class Runtime {
8088
getAppRouter: this.getAppRouter,
8189
setRender: this.setRender,
8290
addWrapper: this.addWrapper,
91+
addSuspenseWrapper: this.addSuspenseWrapper,
8392
appContext: this.appContext,
8493
setAppRouter: this.setAppRouter,
8594
useData: process.env.ICE_CORE_ROUTER === 'true' ? useData : useSingleRouterData,
@@ -122,6 +131,12 @@ class Runtime {
122131
});
123132
};
124133

134+
private addSuspenseWrapper: AddSuspenseWrapper = (Wrapper) => {
135+
this.SuspenseWrappers.push({
136+
Wrapper,
137+
});
138+
};
139+
125140
public setAppRouter: SetAppRouter = (AppRouter) => {
126141
this.AppRouter = AppRouter;
127142
};

packages/runtime/src/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export interface AppContext {
117117
loaderData?: LoadersData;
118118
routeModules?: RouteModules;
119119
RouteWrappers?: RouteWrapperConfig[];
120+
SuspenseWrappers?: SuspenseWrapperConfig[];
120121
routePath?: string;
121122
matches?: RouteMatch[];
122123
routes?: RouteItem[];
@@ -187,16 +188,26 @@ export interface RouteWrapperConfig {
187188

188189
export type AppProvider = ComponentWithChildren<any>;
189190
export type RouteWrapper = ComponentType<any>;
191+
192+
export type SuspenseWrapper = ComponentWithChildren<{
193+
id: string;
194+
}>;
195+
190196
export type ResponseHandler = (
191197
req: IncomingMessage,
192198
res: ServerResponse,
193199
) => any | Promise<any>;
194200

201+
export interface SuspenseWrapperConfig {
202+
Wrapper: SuspenseWrapper;
203+
}
204+
195205
export type SetAppRouter = <T>(AppRouter: ComponentType<T>) => void;
196206
export type GetAppRouter = () => AppProvider;
197207
export type AddProvider = (Provider: AppProvider) => void;
198208
export type SetRender = (render: Renderer) => void;
199209
export type AddWrapper = (wrapper: RouteWrapper, forLayout?: boolean) => void;
210+
export type AddSuspenseWrapper = (wrapper: SuspenseWrapper) => void;
200211
export type AddResponseHandler = (handler: ResponseHandler) => void;
201212
export type GetResponseHandlers = () => ResponseHandler[];
202213

@@ -227,6 +238,7 @@ export interface RuntimeAPI {
227238
getResponseHandlers: GetResponseHandlers;
228239
setRender: SetRender;
229240
addWrapper: AddWrapper;
241+
addSuspenseWrapper: AddSuspenseWrapper;
230242
appContext: AppContext;
231243
useData: UseData;
232244
useConfig: UseConfig;

0 commit comments

Comments
 (0)