Skip to content

Commit 4a3840d

Browse files
authored
feat: deferred queries
feat: deferred queries in loader
2 parents 1483f1a + 3bb14fc commit 4a3840d

File tree

6 files changed

+126
-9
lines changed

6 files changed

+126
-9
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,35 @@ const Component = () => {
199199
};
200200
```
201201

202+
## Deferring queries
203+
204+
You can defer queries by using the `deferredQueries` argument in `createLoader` (or `createUseLoader`). These queries are passed as the second argument to `transform` which has to be used to access the deferred queries in your loaded component.
205+
206+
Example usage:
207+
208+
```tsx
209+
const loader = createLoader({
210+
queries: () => [useImportantQuery()] as const,
211+
deferredQueries: () => [useSlowButNotImportantQuery()] as const,
212+
transform: (queries, deferredQueries) => ({
213+
important: queries[0].data,
214+
not_important: deferredQueries[0].data,
215+
}),
216+
});
217+
218+
const Component = withLoader((props, loaderData) => {
219+
const { important, not_important } = loaderData;
220+
// not_important could be undefined
221+
222+
return (
223+
<div>
224+
{important.person.name}
225+
{not_important ? "it has resolved : "some fallback"}
226+
</div>
227+
)
228+
}, loader);
229+
```
230+
202231
## InferLoaderData
203232
204233
Infers the type of the data the loader returns. Use:

deploy.mjs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env zx
2+
try {
3+
await Promise.all([
4+
$`yarn build`,
5+
$`npm version patch --force`,
6+
]);
7+
const version = require("./package.json").version;
8+
await Promise.all([
9+
$`git commit --allow-empty -m "version: ${version}"`,
10+
$`git push`,
11+
$`npm publish --access public`,
12+
$`echo "======================"`,
13+
$`echo "Deployed! 🚀 (${version})"`,
14+
$`echo "======================"`,
15+
]);
16+
} catch (err) {
17+
await Promise.all([
18+
$`echo "======================"`,
19+
$`echo "Deploy failed! 😭"`,
20+
$`echo "======================"`,
21+
$`echo "${err}"`,
22+
]);
23+
}

src/createLoader.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@ import * as Types from "./types";
44

55
export const createUseLoader = <
66
QRU extends readonly Types.UseQueryResult<unknown>[],
7+
QRUD extends readonly Types.UseQueryResult<unknown>[],
78
R extends unknown = Types.MakeDataRequired<QRU>,
89
A = never
910
>(
10-
createUseLoaderArgs: Types.CreateUseLoaderArgs<QRU, R, A>
11+
createUseLoaderArgs: Types.CreateUseLoaderArgs<QRU, QRUD, R, A>
1112
): Types.UseLoader<A, R> => {
1213
const useLoader = (...args: Types.OptionalGenericArg<A>) => {
1314
const createdQueries = createUseLoaderArgs.queries(...args);
15+
const deferredQueries =
16+
createUseLoaderArgs.deferredQueries?.(...args) ?? [];
1417
const aggregatedQuery = aggregateToQuery(createdQueries);
1518

1619
if (aggregatedQuery.isSuccess) {
1720
const data = createUseLoaderArgs.transform
1821
? createUseLoaderArgs.transform(
19-
createdQueries as unknown as Types.MakeDataRequired<QRU>
22+
createdQueries as unknown as Types.MakeDataRequired<QRU>,
23+
deferredQueries as QRUD
2024
)
2125
: createdQueries;
2226

@@ -36,15 +40,17 @@ export const createUseLoader = <
3640
export const createLoader = <
3741
P extends unknown,
3842
QRU extends readonly Types.UseQueryResult<unknown>[] = [],
43+
QRUD extends readonly Types.UseQueryResult<unknown>[] = [],
3944
R extends unknown = Types.MakeDataRequired<QRU>,
4045
A = never
4146
>(
42-
createLoaderArgs: Types.CreateLoaderArgs<P, QRU, R, A>
47+
createLoaderArgs: Types.CreateLoaderArgs<P, QRU, QRUD, R, A>
4348
): Types.Loader<P, R, QRU, A> => {
4449
const useLoader = createUseLoader({
4550
queries:
4651
createLoaderArgs.queries ?? (() => [] as unknown as QRU),
4752
transform: createLoaderArgs.transform,
53+
deferredQueries: createLoaderArgs.deferredQueries,
4854
});
4955

5056
const loader: Types.Loader<P, R, QRU, A> = {
@@ -58,6 +64,7 @@ export const createLoader = <
5864
createLoaderArgs.loaderComponent ?? RTKLoader,
5965
extend: function <
6066
QRUb extends readonly Types.UseQueryResult<unknown>[],
67+
QRUDb extends readonly Types.UseQueryResult<unknown>[],
6168
Pb extends unknown = P,
6269
Rb = QRUb extends unknown
6370
? R
@@ -67,7 +74,9 @@ export const createLoader = <
6774
queries,
6875
transform,
6976
...loaderArgs
70-
}: Partial<Types.CreateLoaderArgs<Pb, QRUb, Rb, Ab>>) {
77+
}: Partial<
78+
Types.CreateLoaderArgs<Pb, QRUb, QRUDb, Rb, Ab>
79+
>) {
7180
const extendedLoader = {
7281
...(this as unknown as Types.Loader<Pb, Rb, QRUb, Ab>),
7382
...loaderArgs,

src/types.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ export type OptionalGenericArg<T> = T extends never ? [] : [T];
5252

5353
export type LoaderTransformFunction<
5454
QRU extends readonly UseQueryResult<unknown>[],
55+
QRUD extends readonly UseQueryResult<unknown>[],
5556
R extends unknown
56-
> = (queries: MakeDataRequired<QRU>) => R;
57+
> = (queries: MakeDataRequired<QRU>, deferredQueries: QRUD) => R;
5758

5859
export type CreateUseLoaderArgs<
5960
QRU extends readonly UseQueryResult<unknown>[],
61+
QRUD extends readonly UseQueryResult<unknown>[],
6062
R extends unknown,
6163
A = never
6264
> = {
@@ -70,8 +72,18 @@ export type CreateUseLoaderArgs<
7072
* ```
7173
*/
7274
queries: (...args: OptionalGenericArg<A>) => QRU;
75+
/** Should return a list of RTK useQuery results.
76+
* Example:
77+
* ```typescript
78+
* (args: Args) => [
79+
* useGetPokemonQuery(args.pokemonId),
80+
* useGetSomethingElse(args.someArg)
81+
* ] as const
82+
* ```
83+
*/
84+
deferredQueries?: (...args: OptionalGenericArg<A>) => QRUD;
7385
/** Transforms the output of the queries */
74-
transform?: LoaderTransformFunction<QRU, R>;
86+
transform?: LoaderTransformFunction<QRU, QRUD, R>;
7587
};
7688

7789
export type UseLoader<A, R> = (
@@ -136,9 +148,10 @@ export type CustomLoaderProps<T = unknown> = {
136148
export type CreateLoaderArgs<
137149
P extends unknown,
138150
QRU extends readonly UseQueryResult<unknown>[],
151+
QRUD extends readonly UseQueryResult<unknown>[],
139152
R extends unknown = MakeDataRequired<QRU>,
140153
A = never
141-
> = Partial<CreateUseLoaderArgs<QRU, R, A>> & {
154+
> = Partial<CreateUseLoaderArgs<QRU, QRUD, R, A>> & {
142155
/** Generates an argument for the `queries` based on component props */
143156
queriesArg?: (props: P) => A;
144157
/** Determines what to render while loading (with no data to fallback on) */
@@ -188,6 +201,7 @@ export type Loader<
188201
/** Returns a new `Loader` extended from this `Loader`, with given overrides. */
189202
extend: <
190203
QRUb extends readonly UseQueryResult<unknown>[] = QRU,
204+
QRUDb extends readonly UseQueryResult<unknown>[] = [],
191205
Pb extends unknown = P,
192206
Rb extends unknown = QRUb extends QRU
193207
? R extends never
@@ -196,7 +210,7 @@ export type Loader<
196210
: MakeDataRequired<QRUb>,
197211
Ab = A
198212
>(
199-
newLoader: Partial<CreateLoaderArgs<Pb, QRUb, Rb, Ab>>
213+
newLoader: Partial<CreateLoaderArgs<Pb, QRUb, QRUDb, Rb, Ab>>
200214
) => Loader<Pb, Rb, QRUb extends never ? QRU : QRUb, Ab>;
201215
/** The component to use to switch between rendering the different query states. */
202216
LoaderComponent: Component<CustomLoaderProps>;

testing-app/src/mocks.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ export const handlers = [
2323
if (req.params.name === "error") {
2424
return res(c.delay(RESPONSE_DELAY), c.status(500));
2525
}
26+
const delay =
27+
req.params.name === "delay"
28+
? RESPONSE_DELAY + 100
29+
: RESPONSE_DELAY;
2630
return res(
27-
c.delay(RESPONSE_DELAY),
31+
c.delay(delay),
2832
c.status(200),
2933
c.json({
3034
name: req.params.name,

testing-app/src/tests.test.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,44 @@ describe("withLoader", () => {
152152
);
153153
});
154154

155+
test("Can defer some queries", async () => {
156+
const Component = withLoader(
157+
(props, { charizard, delay }) => {
158+
return (
159+
<>
160+
<div>{charizard.name}</div>
161+
<div>
162+
{delay ? "loaded-deferred" : "loading-deferred"}
163+
</div>
164+
</>
165+
);
166+
},
167+
createLoader({
168+
queries: () =>
169+
[useGetPokemonByNameQuery("charizard")] as const,
170+
deferredQueries: () => {
171+
const delayQ = useGetPokemonByNameQuery("delay");
172+
return [delayQ] as const;
173+
},
174+
transform: (queries, deferred) => ({
175+
charizard: queries[0].data,
176+
delay: deferred[0].data,
177+
}),
178+
onLoading: () => <div>Loading</div>,
179+
onError: () => <div>Error</div>,
180+
})
181+
);
182+
render(<Component />);
183+
expect(screen.getByText("Loading")).toBeVisible();
184+
await waitFor(() =>
185+
expect(screen.getByText("charizard")).toBeVisible()
186+
);
187+
expect(screen.getByText("loading-deferred")).toBeVisible();
188+
await waitFor(() =>
189+
expect(screen.getByText("loaded-deferred")).toBeVisible()
190+
);
191+
});
192+
155193
describe(".extend()", () => {
156194
test("Can extend onLoading", async () => {
157195
render(<ExtendedLoaderComponent />);

0 commit comments

Comments
 (0)