diff --git a/src/routes/EntryPointRoute.tsx b/src/routes/EntryPointRoute.tsx index dc37b0a..b8a53d4 100644 --- a/src/routes/EntryPointRoute.tsx +++ b/src/routes/EntryPointRoute.tsx @@ -15,11 +15,13 @@ import { InternalPreload } from "./internal-preload-symbol"; const preloadsToDispose = new Set(); +type InternalPreloadProperties = { + entryPoint: () => Promise>; + resource: () => Promise; +}; + export type PreloadableComponent = ComponentType & { - [InternalPreload]?: { - entryPoint: () => Promise>; - resource: () => Promise; - }; + [InternalPreload]?: InternalPreloadProperties; }; export default function EntryPointRoute( @@ -84,7 +86,7 @@ export default function EntryPointRoute( // This would be much better if it injected a modulepreload link. Unfortunately // we don't have a mechanism for getting the right bundle file name to put into // the href. We might be able to do it by building a rollup plugin. - Hoc[InternalPreload] = { + const internalPreloadProperties: InternalPreloadProperties = { async entryPoint() { return "load" in entryPoint ? entryPoint.load() : entryPoint; }, @@ -93,6 +95,7 @@ export default function EntryPointRoute( return entryPoint.root.load(); }, }; + Hoc[InternalPreload] = internalPreloadProperties; return Hoc; } diff --git a/src/routes/create-entry-point-route.ts b/src/routes/create-entry-point-route.ts index ab4ee39..3caf93b 100644 --- a/src/routes/create-entry-point-route.ts +++ b/src/routes/create-entry-point-route.ts @@ -2,12 +2,13 @@ import { type IEnvironmentProvider, loadQuery, JSResourceReference, - PreloadOptions, + PreloadedQuery, GraphQLTaggedNode, PreloadableConcreteRequest, + PreloadOptions, EnvironmentProviderOptions, -} from "react-relay"; -import type { LoaderFunction, LoaderFunctionArgs } from "react-router-dom"; +} from 'react-relay'; +import type { LoaderFunction, LoaderFunctionArgs, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs } from "react-router-dom"; import type { ComponentType } from "react"; import type { @@ -16,9 +17,12 @@ import type { SimpleEntryPoint, } from "./entry-point.types"; import EntryPointRoute from "./EntryPointRoute"; +import type {EntryPointRouteObject} from './entry-point-route-object.types'; +import {OperationType} from 'relay-runtime'; type EntryPointRouteProperties = { loader: LoaderFunction; + shouldRevalidate: ShouldRevalidateFunction; Component: ComponentType>; }; @@ -29,9 +33,12 @@ export function createEntryPointRoute< entryPoint: | SimpleEntryPoint | JSResourceReference>, + rest: Omit, environmentProvider: IEnvironmentProvider, - contextProvider?: PreloaderContextProvider + contextProvider?: PreloaderContextProvider, ): EntryPointRouteProperties { + let queries: {[p: string]: PreloadedQuery} | undefined = undefined; + async function loader(args: LoaderFunctionArgs): Promise { const loadedEntryPoint = "load" in entryPoint ? await entryPoint.load() : entryPoint; @@ -39,7 +46,6 @@ export function createEntryPointRoute< ...args, preloaderContext: contextProvider?.getPreloaderContext() as any, }); - let queries = undefined; if (queryArgs) { queries = Object.fromEntries( Object.entries(queryArgs).map( @@ -80,8 +86,23 @@ export function createEntryPointRoute< }; } + // This is needed to avoid cases where the query has been disposed of but the + // router would not normally revalidate and rerun the loader which is needed + // to reload the query. + // See https://github.com/loop-payments/react-router-relay/issues/15. + function shouldRevalidate(args: ShouldRevalidateFunctionArgs): boolean { + for (let key in queries) { + const query = queries[key] + if (query.isDisposed) { + return true; + } + } + return rest.shouldRevalidate?.(args) ?? args.defaultShouldRevalidate; + } + return { loader, + shouldRevalidate, Component: EntryPointRoute(entryPoint), }; } diff --git a/src/routes/prepare-preloadable-routes.ts b/src/routes/prepare-preloadable-routes.ts index 1470d82..6a83992 100644 --- a/src/routes/prepare-preloadable-routes.ts +++ b/src/routes/prepare-preloadable-routes.ts @@ -31,6 +31,7 @@ export function preparePreloadableRoutes( ...rest, ...createEntryPointRoute( entryPoint, + rest, environmentProvider, preloaderContextProvider, ),