Skip to content

Commit f17dafb

Browse files
dai-shitylersayshi
andauthored
fix(router): fallback support in new_defineRouter and new_createPages (#1013)
add fallback mechanism for #1003. --------- Co-authored-by: Tyler <[email protected]> Co-authored-by: Tyler <[email protected]>
1 parent 00916cc commit f17dafb

File tree

7 files changed

+66
-17
lines changed

7 files changed

+66
-17
lines changed

e2e/broken-link.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async function start(staticServe: boolean) {
3030
]);
3131

3232
await waitPort({ port });
33-
return [port, cp.pid];
33+
return [port, cp.pid] as const;
3434
}
3535

3636
test.beforeEach(async () => {

e2e/ssr-catch-error.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async function run(isDev: boolean) {
3333
/ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time/,
3434
]);
3535
await waitPort({ port });
36-
return [port, cp.pid];
36+
return [port, cp.pid] as const;
3737
}
3838

3939
for (const isDev of [true, false]) {

e2e/use-router.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function start() {
2828
]);
2929

3030
await waitPort({ port });
31-
return [port, cp.pid];
31+
return [port, cp.pid] as const;
3232
}
3333

3434
test.describe('useRouter', async () => {

packages/waku/src/minimal/client.ts

+37-9
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,10 @@ const ChildrenContextProvider = memo(ChildrenContext.Provider);
224224

225225
type OuterSlotProps = {
226226
elementsPromise: Elements;
227-
shouldRenderPrev: ((err: unknown) => boolean) | undefined;
228-
renderSlot: (elements: Record<string, ReactNode>) => ReactNode;
227+
unstable_shouldRenderPrev:
228+
| ((err: unknown, prevElements: Record<string, ReactNode>) => boolean)
229+
| undefined;
230+
renderSlot: (elements: Record<string, ReactNode>, err?: unknown) => ReactNode;
229231
children?: ReactNode;
230232
};
231233

@@ -245,9 +247,12 @@ class OuterSlot extends Component<OuterSlotProps, { error?: unknown }> {
245247
// probably caused by history api fallback
246248
(e as any).statusCode = 404;
247249
}
248-
if (this.props.shouldRenderPrev?.(e) && this.props.elementsPromise.prev) {
249-
const elements = this.props.elementsPromise.prev;
250-
return this.props.renderSlot(elements);
250+
const prevElements = this.props.elementsPromise.prev;
251+
if (
252+
prevElements &&
253+
this.props.unstable_shouldRenderPrev?.(e, prevElements)
254+
) {
255+
return this.props.renderSlot(prevElements, e);
251256
} else {
252257
throw e;
253258
}
@@ -261,12 +266,16 @@ const InnerSlot = ({
261266
renderSlot,
262267
}: {
263268
elementsPromise: Elements;
264-
renderSlot: (elements: Record<string, ReactNode>) => ReactNode;
269+
renderSlot: (elements: Record<string, ReactNode>, err?: unknown) => ReactNode;
265270
}) => {
266271
const elements = use(elementsPromise);
267272
return renderSlot(elements);
268273
};
269274

275+
const InnerErr = ({ err }: { err: unknown }) => {
276+
throw err;
277+
};
278+
270279
/**
271280
* Slot component
272281
* This is used under the Root component.
@@ -286,19 +295,32 @@ export const Slot = ({
286295
children,
287296
fallback,
288297
unstable_shouldRenderPrev,
298+
unstable_renderPrev,
289299
}: {
290300
id: string;
291301
children?: ReactNode;
292302
fallback?: ReactNode;
293-
unstable_shouldRenderPrev?: (err: unknown) => boolean;
303+
unstable_shouldRenderPrev?: (
304+
err: unknown,
305+
prevElements: Record<string, ReactNode>,
306+
) => boolean;
307+
unstable_renderPrev?: boolean;
294308
}) => {
295309
const elementsPromise = use(ElementsContext);
296310
if (!elementsPromise) {
297311
throw new Error('Missing Root component');
298312
}
299-
const renderSlot = (elements: Record<string, ReactNode>) => {
313+
const renderSlot = (elements: Record<string, ReactNode>, err?: unknown) => {
300314
if (!(id in elements)) {
301315
if (fallback) {
316+
if (err) {
317+
// HACK I'm not sure if this is the right way
318+
return createElement(
319+
ChildrenContextProvider,
320+
{ value: createElement(InnerErr, { err }) },
321+
fallback,
322+
);
323+
}
302324
return fallback;
303325
}
304326
throw new Error('Not found: ' + id);
@@ -309,11 +331,17 @@ export const Slot = ({
309331
elements[id],
310332
);
311333
};
334+
if (unstable_renderPrev) {
335+
if (!elementsPromise.prev) {
336+
throw new Error('Missing prev elements');
337+
}
338+
return renderSlot(elementsPromise.prev);
339+
}
312340
return createElement(
313341
OuterSlot,
314342
{
315343
elementsPromise,
316-
shouldRenderPrev: unstable_shouldRenderPrev,
344+
unstable_shouldRenderPrev,
317345
renderSlot,
318346
},
319347
createElement(InnerSlot, { elementsPromise, renderSlot }),

packages/waku/src/router/client.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ const RouterSlot = ({
322322
fallback?: ReactNode;
323323
children?: ReactNode;
324324
}) => {
325-
const unstable_shouldRenderPrev = (_err: unknown) => {
325+
const unstable_shouldRenderPrev = () => {
326326
const shouldSkip = routerData[0];
327327
const skip = getSkipList(shouldSkip, [id], route, cachedRef.current);
328328
return skip.length > 0;
@@ -758,7 +758,16 @@ const NewInnerRouter = ({
758758
});
759759
});
760760

761-
const routeElement = createElement(Slot, { id: getRouteSlotId(route.path) });
761+
const routeElement = createElement(Slot, {
762+
id: getRouteSlotId(route.path),
763+
unstable_shouldRenderPrev: (_err, prevElements) =>
764+
// HACK this might not work in some cases
765+
'fallback' in prevElements,
766+
fallback: createElement(Slot, {
767+
id: 'fallback',
768+
unstable_renderPrev: true,
769+
}),
770+
});
762771

763772
return createElement(
764773
RouterContext.Provider,

packages/waku/src/router/create-pages.ts

+9
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,15 @@ export const new_createPages = <
826826
{ id: 'root' },
827827
createNestedElements(routeChildren),
828828
),
829+
// HACK this is hard-coded convention
830+
// FIXME we should revisit the error boundary use case design
831+
fallbackElement: createElement(
832+
Slot,
833+
{ id: 'root', unstable_renderPrev: true },
834+
layoutPaths.includes('/')
835+
? createElement(Slot, { id: 'layout:/', unstable_renderPrev: true })
836+
: null,
837+
),
829838
};
830839
},
831840
});

packages/waku/src/router/define-router.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -302,13 +302,13 @@ export function new_defineRouter(fns: {
302302
) => Promise<{
303303
routeElement: ReactNode;
304304
elements: Record<SlotId, ReactNode>;
305+
fallbackElement?: ReactNode;
305306
}>;
306307
}): ReturnType<typeof defineEntries> {
307308
const platformObject = unstable_getPlatformObject();
308309
type MyPathConfig = {
309310
pattern: string;
310311
pathname: PathSpec;
311-
isStaticRouteElement: boolean;
312312
staticElementIds: SlotId[];
313313
isStatic?: boolean | undefined;
314314
specs: { noSsr?: boolean; is404: boolean };
@@ -328,7 +328,6 @@ export function new_defineRouter(fns: {
328328
return {
329329
pattern: item.pattern,
330330
pathname: item.path,
331-
isStaticRouteElement: !!item.routeElement.isStatic,
332331
staticElementIds: Object.entries(item.elements).flatMap(
333332
([id, { isStatic }]) => (isStatic ? [id] : []),
334333
),
@@ -393,7 +392,7 @@ export function new_defineRouter(fns: {
393392
return null;
394393
}
395394
const { query, skip } = parseRscParams(rscParams);
396-
const { routeElement, elements } = await fns.renderRoute(
395+
const { routeElement, elements, fallbackElement } = await fns.renderRoute(
397396
pathname,
398397
pathStatus.isStatic ? {} : { query },
399398
);
@@ -405,6 +404,10 @@ export function new_defineRouter(fns: {
405404
const entries = {
406405
...elements,
407406
[ROUTE_SLOT_ID_PREFIX + pathname]: routeElement,
407+
...((fallbackElement ? { fallback: fallbackElement } : {}) as Record<
408+
string,
409+
ReactNode
410+
>),
408411
};
409412
for (const skipId of await filterEffectiveSkip(pathname, skip)) {
410413
delete entries[skipId];

0 commit comments

Comments
 (0)