diff --git a/packages/models/src/react/provideReact.test.tsx b/packages/models/src/react/provideReact.test.tsx index 5df42fca..0bb22874 100644 --- a/packages/models/src/react/provideReact.test.tsx +++ b/packages/models/src/react/provideReact.test.tsx @@ -28,8 +28,15 @@ beforeEach(() => { }); class TestModel extends ReferenceModel { - public static ofId(id: number) { - return new TestModel(String(id)); + public readonly lazyDependency: number; + + public static ofId(id: number, lazyDependency = 0) { + return new TestModel(String(id), lazyDependency); + } + + public constructor(id: string, lazyDependency: number) { + super(id); + this.lazyDependency = lazyDependency; } public getDetailed = provideReact(async () => { @@ -39,11 +46,14 @@ class TestModel extends ReferenceModel { id: this.id, foo: true, }; - }, [this.id]); + }, [this.id, () => this.lazyDependency]); } -const TestComponent: FC<{ id: number }> = (props) => { - const model = TestModel.ofId(props.id).getDetailed.use(); +const TestComponent: FC<{ id: number; lazyDependency?: number }> = (props) => { + const model = TestModel.ofId( + props.id, + props.lazyDependency, + ).getDetailed.use(); return {model.id}; }; @@ -53,10 +63,14 @@ const TestWrapper: FC = (props) => ( ); -const runTest = async (id: number, expectedDataLoadingCount: number) => { +const runTest = async ( + id: number, + expectedDataLoadingCount: number, + dynamicId = 0, +) => { const ui = rerender - ? rerender() - : render(, { + ? rerender() + : render(, { wrapper: TestWrapper, }); @@ -79,6 +93,13 @@ test("Model caches data", async () => { await runTest(43, 2); }); +test("Model caches data with lazy dependency", async () => { + await runTest(43, 1); + await runTest(43, 2, 42); + await runTest(43, 2, 42); + await runTest(43, 3, 43); +}); + test("Model cache can be refreshed", async () => { await runTest(42, 1); // Tag does not exist diff --git a/packages/models/src/react/provideReact.ts b/packages/models/src/react/provideReact.ts index cb3f4d2a..31f6be85 100644 --- a/packages/models/src/react/provideReact.ts +++ b/packages/models/src/react/provideReact.ts @@ -6,14 +6,30 @@ import { joinedId } from "../lib/joinedId.js"; type AsyncFn = (...args: any[]) => Promise; +type ReactProvisionDep = string | number; +type LazyReactProvisionDep = () => ReactProvisionDep; +type ReactProvisionDeps = Array; + export const provideReact = ( loader: T, - dependencies: string[] = [], + dependencies: ReactProvisionDeps = [], ) => { type P = Parameters; - const provisionId = joinedId(hash(loader), ...dependencies); + const loaderHash = hash(loader); + let cachedProvisionId: string | undefined; const getAsyncResource = (params: P) => { + const provisionId = + cachedProvisionId ?? + joinedId( + loaderHash, + ...dependencies + .map((d) => (typeof d === "function" ? d() : d)) + .map((d) => hash(d)), + ); + + cachedProvisionId = provisionId; + const contextId = joinedId(provisionId, hash(params)); const loaderWithContext = reactProvisionContext.bind(