diff --git a/packages/inferno-router/__tests__/loaderOnRoute.spec.tsx b/packages/inferno-router/__tests__/loaderOnRoute.spec.tsx
index 9ac444ba7..5ce617d40 100644
--- a/packages/inferno-router/__tests__/loaderOnRoute.spec.tsx
+++ b/packages/inferno-router/__tests__/loaderOnRoute.spec.tsx
@@ -693,4 +693,44 @@ describe('Resolve loaders during server side rendering', () => {
const result = await resolveLoaders(loaderEntries);
expect(result).toEqual(initialData);
});
+
+
+ it('Can resolve with sub classed Route', async () => {
+ class MyRoute extends Route {
+ constructor(props, context) {
+ super(props, context);
+ }
+ }
+ const TEXT = 'bubblegum';
+ const Component = (props) => {
+ const res = useLoaderData(props);
+ return
{res?.message}
;
+ };
+
+ const loaderFuncNoHit = async () => {
+ return { message: 'no' };
+ };
+ const loaderFunc = async () => {
+ return { message: TEXT };
+ };
+
+ const initialData = {
+ '/flowers': { res: await loaderFunc() },
+ '/flowers/birds': { res: await loaderFunc() }
+ };
+
+ const app = (
+
+
+
+
+ {null}
+
+
+ );
+
+ const loaderEntries = traverseLoaders('/flowers/birds', app);
+ const result = await resolveLoaders(loaderEntries);
+ expect(result).toEqual(initialData);
+ });
});
diff --git a/packages/inferno-router/__tests__/loaderWithSwitch.spec.tsx b/packages/inferno-router/__tests__/loaderWithSwitch.spec.tsx
index 682b65683..c6798ba8e 100644
--- a/packages/inferno-router/__tests__/loaderWithSwitch.spec.tsx
+++ b/packages/inferno-router/__tests__/loaderWithSwitch.spec.tsx
@@ -1,12 +1,5 @@
import { render, rerender } from 'inferno';
-import {
- MemoryRouter,
- Route,
- Switch,
- NavLink,
- useLoaderData,
- useLoaderError,
-} from 'inferno-router';
+import { MemoryRouter, StaticRouter, Route, Switch, NavLink, useLoaderData, useLoaderError, resolveLoaders, traverseLoaders } from 'inferno-router';
// Cherry picked relative import so we don't get node-stuff from inferno-server in browser test
import { createEventGuard } from './testUtils';
@@ -44,7 +37,7 @@ describe('A with loader in a MemoryRouter', () => {
loader={loaderFunc}
/>
,
- container,
+ container
);
// Wait until async loader has completed
@@ -73,7 +66,7 @@ describe('A with loader in a MemoryRouter', () => {
loader={loaderFunc}
/>
,
- container,
+ container
);
// Wait until async loader has completed
@@ -92,14 +85,14 @@ describe('A with loader in a MemoryRouter', () => {
return { message: TEXT };
};
const initialData = {
- '/flowers': { res: await loaderFunc(), err: undefined },
+ '/flowers': { res: await loaderFunc(), err: undefined }
};
render(
,
- container,
+ container
);
expect(container.innerHTML).toContain(TEXT);
@@ -178,14 +171,14 @@ describe('A with loader in a MemoryRouter', () => {
return { message: TEXT };
};
const initialData = {
- '/flowers': { res: await loaderFunc(), err: undefined },
+ '/flowers': { res: await loaderFunc(), err: undefined }
};
render(
,
- container,
+ container
);
expect(container.innerHTML).toContain(TEXT);
@@ -283,7 +276,7 @@ describe('A with loader in a MemoryRouter', () => {
/>
,
- container,
+ container
);
// Check that we are starting in the right place
@@ -371,3 +364,102 @@ describe('A with loader in a MemoryRouter', () => {
expect(container.querySelector('#publish')).toBeNull();
});
});
+
+describe('Resolve loaders during server side rendering', () => {
+ it('Can resolve with single route', async () => {
+ const TEXT = 'bubblegum';
+ const Component = (props) => {
+ const res = useLoaderData(props);
+ return {res?.message}
;
+ };
+
+ const loaderFunc = async () => {
+ return { message: TEXT };
+ };
+
+ const initialData = {
+ '/flowers': { res: await loaderFunc() }
+ };
+
+ const app = (
+
+
+
+
+
+ );
+
+ const loaderEntries = traverseLoaders('/flowers', app);
+ const result = await resolveLoaders(loaderEntries);
+ expect(result).toEqual(initialData);
+ });
+
+ it('Can resolve with multiple routes', async () => {
+ const TEXT = 'bubblegum';
+ const Component = (props) => {
+ const res = useLoaderData(props);
+ return {res?.message}
;
+ };
+
+ const loaderFuncNoHit = async () => {
+ return { message: 'no' };
+ };
+ const loaderFunc = async () => {
+ return { message: TEXT };
+ };
+
+ const initialData = {
+ '/birds': { res: await loaderFunc() }
+ };
+
+ const app = (
+
+
+
+
+
+
+
+ );
+
+ const loaderEntries = traverseLoaders('/birds', app);
+ const result = await resolveLoaders(loaderEntries);
+ expect(result).toEqual(initialData);
+ });
+
+ it('Can resolve with nested routes', async () => {
+ const TEXT = 'bubblegum';
+ const Component = (props) => {
+ const res = useLoaderData(props);
+ return {res?.message}
;
+ };
+
+ const loaderFuncNoHit = async () => {
+ return { message: 'no' };
+ };
+ const loaderFunc = async () => {
+ return { message: TEXT };
+ };
+
+ const initialData = {
+ '/flowers': { res: await loaderFunc() },
+ '/flowers/birds': { res: await loaderFunc() }
+ };
+
+ const app = (
+
+
+
+
+
+ {null}
+
+
+
+ );
+
+ const loaderEntries = traverseLoaders('/flowers/birds', app);
+ const result = await resolveLoaders(loaderEntries);
+ expect(result).toEqual(initialData);
+ });
+});
\ No newline at end of file
diff --git a/packages/inferno-router/src/resolveLoaders.ts b/packages/inferno-router/src/resolveLoaders.ts
index 7747c4319..a3f586c2b 100644
--- a/packages/inferno-router/src/resolveLoaders.ts
+++ b/packages/inferno-router/src/resolveLoaders.ts
@@ -1,7 +1,8 @@
import { isNullOrUndef, isUndefined } from 'inferno-shared';
import { matchPath } from './matchPath';
-import type { TLoaderData } from './Router';
+import type { TLoaderData, TLoaderProps } from './Router';
import { Switch } from './Switch';
+import { Route } from './Route';
export async function resolveLoaders(
loaderEntries: TLoaderEntry[],
@@ -21,7 +22,7 @@ interface TLoaderEntry {
params: Record;
request: Request;
controller: AbortController;
- loader: (TLoaderProps) => Promise;
+ loader: (props: TLoaderProps) => Promise;
}
export function traverseLoaders(
@@ -32,6 +33,19 @@ export function traverseLoaders(
return _traverseLoaders(location, tree, base, false);
}
+function _isSwitch(node: any): boolean {
+ // Using the same patterns as for _isRoute, but I don't have a test where
+ // I pass a Switch via an array, but it is better to be consistent.
+ return node?.type?.prototype instanceof Switch || node?.type === Switch;
+}
+
+function _isRoute(node: any): boolean {
+ // So the === check is needed if routes are passed in an array,
+ // the instanceof test if routes are passed as children to a Component
+ // This feels inconsistent, but at least it works.
+ return node?.type?.prototype instanceof Route || node?.type === Route;
+}
+
// Optionally pass base param during SSR to get fully qualified request URI passed to loader in request param
function _traverseLoaders(
location: string,
@@ -47,12 +61,7 @@ function _traverseLoaders(
const entriesOfArr = tree.reduce((res, node) => {
if (parentIsSwitch && hasMatch) return res;
- const outpArr = _traverseLoaders(
- location,
- node,
- base,
- node?.type?.prototype instanceof Switch,
- );
+ const outpArr = _traverseLoaders(location, node, base, _isSwitch(node));
if (parentIsSwitch && outpArr.length > 0) {
hasMatch = true;
}
@@ -62,9 +71,7 @@ function _traverseLoaders(
}
const outp: TLoaderEntry[] = [];
- let isRouteButNotMatch = false;
- if (tree.props) {
- // TODO: If we traverse a switch, only the first match should be returned
+ if (_isRoute(tree) && tree.props) {
// TODO: Should we check if we are in Router? It is defensive and could save a bit of time, but is it worth it?
const {
path,
@@ -80,10 +87,10 @@ function _traverseLoaders(
});
// So we can bail out of recursion it this was a Route which didn't match
- isRouteButNotMatch = !match;
-
- // Add any loader on this node (but only on the VNode)
- if (match && !tree.context && tree.props?.loader && tree.props?.path) {
+ if (!match) {
+ return outp;
+ } else if (!tree.context && tree.props?.loader && tree.props?.path) {
+ // Add any loader on this node (but only on the VNode)
const { params } = match;
const controller = new AbortController();
const request = createClientSideRequest(
@@ -102,16 +109,11 @@ function _traverseLoaders(
}
}
- // Traverse ends here
- if (isRouteButNotMatch) return outp;
-
// Traverse children
- const entries = _traverseLoaders(
- location,
- tree.children || tree.props?.children,
- base,
- tree.type?.prototype instanceof Switch,
- );
+ const children = tree.children ?? tree.props?.children;
+ if (isNullOrUndef(children)) return outp;
+
+ const entries = _traverseLoaders(location, children, base, _isSwitch(tree));
return [...outp, ...entries];
}
@@ -214,7 +216,7 @@ function createClientSideRequest(
// Request is undefined when running tests
if (process.env.NODE_ENV === 'test' && typeof Request === 'undefined') {
- // @ts-expect-error global req
+ // @ts-expect-error minimum to fix tests
global.Request = class Request {
public url;
public signal;