Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(client): enable to render previous element in slot (experimental) #696

Merged
merged 5 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions packages/waku/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,43 @@ const checkStatus = async (
return response;
};

type Elements = Promise<Record<string, ReactNode>>;
type Elements = Promise<Record<string, ReactNode>> & {
prev?: Record<string, ReactNode> | undefined;
};

const getCached = <T>(c: () => T, m: WeakMap<object, T>, k: object): T =>
(m.has(k) ? m : m.set(k, c())).get(k) as T;
const cache1 = new WeakMap();
const mergeElements = (
a: Elements,
b: Elements | Awaited<Elements>,
): Elements => {
const getResult = async () => {
const nextElements = { ...(await a), ...(await b) };
delete nextElements._value;
return nextElements;
const mergeElements = (a: Elements, b: Elements): Elements => {
const getResult = () => {
const promise: Elements = new Promise((resolve, reject) => {
Promise.all([a, b])
.then(([a, b]) => {
const nextElements = { ...a, ...b };
delete nextElements._value;
promise.prev = a;
resolve(nextElements);
})
.catch((e) => {
a.then(
(a) => {
promise.prev = a;
reject(e);
},
() => {
promise.prev = a.prev;
reject(e);
},
);
});
});
return promise;
};
const cache2 = getCached(() => new WeakMap(), cache1, a);
return getCached(getResult, cache2, b);
};

type SetElements = (updater: Elements | ((prev: Elements) => Elements)) => void;
type SetElements = (updater: (prev: Elements) => Elements) => void;
type CacheEntry = [
input: string,
searchParamsString: string,
Expand Down Expand Up @@ -191,10 +209,12 @@ export const Slot = ({
id,
children,
fallback,
unstable_shouldRenderPrev,
}: {
id: string;
children?: ReactNode;
fallback?: ReactNode;
unstable_shouldRenderPrev?: (err: unknown) => boolean;
}) => {
const elementsPromise = use(ElementsContext);
if (!elementsPromise) {
Expand All @@ -204,12 +224,16 @@ export const Slot = ({
try {
elements = use(elementsPromise);
} catch (e) {
if (e instanceof Error) {
if (e instanceof Error && !('statusCode' in e)) {
// HACK we assume any error as Not Found,
// probably caused by history api fallback
(e as any).statusCode = 404;
}
throw e;
if (unstable_shouldRenderPrev?.(e) && elementsPromise.prev) {
elements = elementsPromise.prev;
} else {
throw e;
}
}
if (!(id in elements)) {
if (fallback) {
Expand Down
39 changes: 36 additions & 3 deletions packages/waku/src/router/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import type {
ComponentProps,
FunctionComponent,
MutableRefObject,
ReactNode,
AnchorHTMLAttributes,
ReactElement,
Expand Down Expand Up @@ -286,7 +287,34 @@ const equalRouteProps = (a: RouteProps, b: RouteProps) => {
return true;
};

function InnerRouter({ routerData }: { routerData: RouterData }) {
const RouterSlot = ({
route,
routerData,
cachedRef,
id,
fallback,
children,
}: {
route: RouteProps;
routerData: RouterData;
cachedRef: MutableRefObject<Record<string, RouteProps>>;
id: string;
fallback?: ReactNode;
children?: ReactNode;
}) => {
const unstable_shouldRenderPrev = (_err: unknown) => {
const shouldSkip = routerData[0];
const skip = getSkipList(shouldSkip, [id], route, cachedRef.current);
return skip.length > 0;
};
return createElement(
Slot,
{ id, fallback, unstable_shouldRenderPrev },
children,
);
};

const InnerRouter = ({ routerData }: { routerData: RouterData }) => {
const refetch = useRefetch();

const [route, setRoute] = useState(() =>
Expand Down Expand Up @@ -418,7 +446,12 @@ function InnerRouter({ routerData }: { routerData: RouterData }) {
});

const children = componentIds.reduceRight(
(acc: ReactNode, id) => createElement(Slot, { id, fallback: acc }, acc),
(acc: ReactNode, id) =>
createElement(
RouterSlot,
{ route, routerData, cachedRef, id, fallback: acc },
acc,
),
null,
);

Expand All @@ -427,7 +460,7 @@ function InnerRouter({ routerData }: { routerData: RouterData }) {
{ value: { route, changeRoute, prefetchRoute } },
children,
);
}
};

// Note: The router data must be a stable mutable object (array).
type RouterData = [
Expand Down
Loading