Skip to content

Commit 390d9d5

Browse files
authored
chore: Merged 1.0.0 update
@ryfylke-react/rtk-query-loader@1.0.0 - [x] **New feature**: Pass any static data through loader - [x] **New feature**: Extend _only_ `transform` without `useQueries` (previously `queries`) - [x] Change: How `createLoader` receives queries. (`useQueries`) - [x] Change: How `createLoader` receives deferredQueries. (`useQueries`) - [x] Change: Default loader output format ```typescript type NewOutputFormat = { queries: Record<string, UseQueryResult>; deferredQueries: Record<string, UseQueryResult>; payload: T; } ``` - [x] Change: You are no longer required to `transform` when deferring queries
2 parents dd943a8 + eccb524 commit 390d9d5

25 files changed

+8461
-336
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,23 @@ import {
2828
} from "@ryfylke-react/rtk-query-loader";
2929

3030
const loader = createLoader({
31-
queries: () => {
31+
useQueries: () => {
3232
const pokemon = useGetPokemon();
3333
const currentUser = useGetCurrentUser();
34-
return [pokemon, currentUser] as const;
34+
35+
return {
36+
queries: {
37+
pokemon,
38+
currentUser,
39+
},
40+
};
3541
},
3642
onLoading: () => <div>Loading pokemon...</div>,
3743
});
3844

39-
const Pokemon = withLoader((props, queries) => {
40-
const pokemon = queries[0].data;
41-
const currentUser = queries[1].data;
45+
const Pokemon = withLoader((props, loader) => {
46+
const pokemon = loader.queries.pokemon.data;
47+
const currentUser = loader.queries.currentUser.data;
4248

4349
return (
4450
<div>
@@ -89,4 +95,5 @@ What if we could instead "join" these queries into one, and then just return ear
8995
- [x] Easy to write re-usable loaders that can be abstracted away from the components
9096

9197
## [Documentation](https://rtk-query-loader.ryfylke.dev)
98+
9299
## [Quick Guide](https://rtk-query-loader.ryfylke.dev/Quick%20Guide/)

docs/docs/Advanced/under-the-hood.md

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
How does `rtk-query-loader` work _under the hood_?
44

5+
## The underlying principles
6+
7+
1. We have a higher-order component (wrapper)
8+
2. We aggregate the status of a collection of queries that are run through a hook
9+
3. We conditionally render the appropriate states
10+
4. We pass the data to the consumer-component through the use of `React.forwardRef`
11+
12+
### Can't I just build this myself?
13+
14+
Yes you can, the concept itself is quite simple. But making it properly type-safe can be difficult, and this package has been thoroughly tested and thought out. And we do offer some killer [features](../Features/index.md).
15+
516
## Virtual DOM
617

718
Imagine you have this component, rendered through `withLoader`:
@@ -59,20 +70,3 @@ div
5970
## RTKLoader
6071

6172
`RTKLoader` simply receives the loader and component, and handles returning the correct state depending on the query. You can take a look at how this component behaves [in the codebase](https://github.com/ryfylke-react-as/rtk-query-loader/blob/main/src/RTKLoader.tsx).
62-
63-
## Custom `loaderComponent`
64-
65-
You could pass a custom `loaderComponent` to your loaders, if you'd like:
66-
67-
```typescript
68-
const CustomLoader = (props: CustomLoaderProps) => {
69-
// Handle rendering
70-
};
71-
72-
const loader = createLoader({
73-
loaderComponent: CustomLoader,
74-
// ...
75-
});
76-
```
77-
78-
This allows you to have really fine control over how the rendering of components using `withLoader` should work.

docs/docs/Exports/create-loader.md

Lines changed: 18 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,22 @@ type ConsumerProps = Record<string, any> & {
1313

1414
const loader = createLoader({
1515
queriesArg: (props: ConsumerProps) => props.userId,
16-
queries: (userId) => {
17-
return [useGetUser(userId)] as const;
16+
useQueries: (userId) => {
17+
return {
18+
queries: {
19+
user: useGetUser(userId),
20+
},
21+
deferredQueries: {
22+
relations: useGetUserRelations(userId),
23+
},
24+
payload: {
25+
// Lets you send any static data to your consumer
26+
static: "data",
27+
},
28+
};
1829
},
19-
deferredQueries: (userId) => {
20-
return [useGetUserRelations(userId)] as const;
21-
},
22-
transform: (queries, deferredQueries) => ({
23-
queries: [...queries, ...deferredQueries],
30+
transform: (loaderData) => ({
31+
...loaderData,
2432
foo: "bar",
2533
}),
2634
onLoading: (props) => <div>Loading...</div>,
@@ -37,57 +45,6 @@ A `Loader` takes ownership over...
3745
- Data-transformation
3846
- Fetch-state rendering
3947

40-
Here is the `createLoader` argument:
41-
42-
````typescript
43-
type CreateLoaderArgs<
44-
P extends unknown, // Props
45-
QRU extends readonly UseQueryResult<unknown>[], // List of queries returned in `queries`
46-
QRUD extends readonly UseQueryResult<unknown>[], // List of queries returned in `deferredQueries`
47-
R extends unknown = MakeDataRequired<QRU>, // Return type
48-
A = never // Loader argument
49-
> = {
50-
/** Should return a list of RTK useQuery results.
51-
* Example:
52-
* ```typescript
53-
* (args: Args) => [
54-
* useGetPokemonQuery(args.pokemonId),
55-
* useGetSomethingElse(args.someArg)
56-
* ] as const
57-
* ```
58-
*/
59-
queries?: (...args: OptionalGenericArg<A>) => QRU;
60-
/** Should return a list of RTK useQuery results.
61-
* Example:
62-
* ```typescript
63-
* (args: Args) => [
64-
* useGetPokemonQuery(args.pokemonId),
65-
* useGetSomethingElse(args.someArg)
66-
* ] as const
67-
* ```
68-
*/
69-
deferredQueries?: (...args: OptionalGenericArg<A>) => QRUD;
70-
/** Transforms the output of the queries */
71-
transform?: LoaderTransformFunction<QRU, QRUD, R>;
72-
/** Generates an argument for the `queries` based on component props */
73-
queriesArg?: (props: P) => A;
74-
/** Determines what to render while loading (with no data to fallback on) */
75-
onLoading?: (props: P) => ReactElement;
76-
/** Determines what to render when query fails. */
77-
onError?: (
78-
props: P,
79-
error: FetchBaseQueryError | SerializedError,
80-
joinedQuery: UseQueryResult<undefined>
81-
) => ReactElement;
82-
/** @deprecated Using onFetching might result in loss of internal state. Use `whileFetching` instead, or pass the query to the component */
83-
onFetching?: (
84-
props: P,
85-
renderBody: () => ReactElement
86-
) => ReactElement;
87-
/** Determines what to render besides success-result while query is fetching. */
88-
whileFetching?: WhileFetchingArgs<P, R>;
89-
/** The component to use to switch between rendering the different query states. */
90-
loaderComponent?: Component<CustomLoaderProps>;
91-
};
92-
```
93-
````
48+
:::caution Using version `0.3.51` or earlier?
49+
Please refer to the [**migration guide**](../Migrations/v0.x).
50+
:::

docs/docs/Exports/use-create-query.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,24 @@ import {
1414
useCreateQuery,
1515
} from "@ryfylke-react/rtk-query-loader";
1616

17+
const getUser = async (userId: string) => {
18+
const res = await fetch(`users/${userId}`);
19+
const json = await res.json();
20+
return json as SomeDataType;
21+
};
22+
1723
const loader = createLoader({
18-
queries: (userId: string) => {
19-
const query = useCreateQuery(async () => {
20-
const res = await fetch(`users/${userId}`);
21-
const json = await res.json();
22-
return json as SomeDataType;
23-
// dependency array
24-
}, [userId]);
25-
return [query] as const;
24+
useQueries: (userId: string) => {
25+
const query = useCreateQuery(
26+
async () => await getUser(userId),
27+
[userId]
28+
);
29+
30+
return {
31+
queries: {
32+
query,
33+
},
34+
};
2635
},
2736
});
2837
```

docs/docs/Features/custom-loader.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
sidebar_position: 5
3+
---
4+
5+
# Custom `loaderComponent`
6+
7+
You could pass a custom `loaderComponent` to your loaders, if you'd like:
8+
9+
```typescript
10+
const CustomLoader = (props: CustomLoaderProps) => {
11+
// Handle rendering
12+
};
13+
14+
const loader = createLoader({
15+
loaderComponent: CustomLoader,
16+
// ...
17+
});
18+
```
19+
20+
:::tip
21+
This allows you to have really fine control over how the rendering of components using `withLoader` should work.
22+
:::

docs/docs/Features/defer-queries.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,29 @@ Say you have a query that takes a long time to resolve. You want to put it in th
88

99
## Example
1010

11-
```typescript {3-7}
11+
```typescript {3-13}
1212
const loader = createLoader({
13-
queries: () => [useImportantQuery()] as const,
14-
deferredQueries: () => [useSlowQuery()] as const,
15-
transform: (queries, deferred) => ({
16-
important: queries[0].data,
17-
slow: deferred[0].data,
13+
useQueries: () => {
14+
const importantQuery = useImportantQuery();
15+
const slowQuery = useSlowQuery();
16+
17+
return {
18+
queries: {
19+
importantQuery,
20+
},
21+
deferredQueries: {
22+
slowQuery,
23+
},
24+
};
25+
},
26+
transform: (loader) => ({
27+
important: loader.queries.importantQuery.data, // ImportantQueryResponse
28+
slow: loader.deferredQueries.slowQuery.data, // SlowQueryResponse | undefined
1829
}),
1930
});
2031
```
2132

22-
Deferred queries are not automatically passed to the output of the loader, you have to use the `transform` argument (and it's second argument) to expose the queries for the consumers. The format of how you transform it is not important, but it is the only way to _get_ the deferred query result out of the loader.
23-
2433
Deferred queries
2534

2635
- Do not affect the loading state
27-
- Are only exposed through `transform`
2836
- Cause the component to rerender when fulfilled

docs/docs/Features/extending.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,37 @@ const parentLoader = createLoader({
1212
});
1313

1414
const childLoader = parentLoader.extend({
15-
queries: () => [...] as const,
16-
})
15+
useQueries: () => ({...}),
16+
onError: () => <div>Error</div>,
17+
}).extend({
18+
transform: () => ({...}),
19+
}).extend({
20+
onLoading: () => <div>Overwritten loading...</div>,
21+
});
1722
```
1823

19-
It's worth mentioning that queries and transform are linked in this context, meaning that if you supply a new queries argument in the extended loader, but no transform, then you will not inherit the transform from the original loader.
20-
21-
- Supplying just a new `queries` argument will result in transform being undefined in practise.
22-
- Supplying just a new `transform` argument will result in the new transform being ignored.
23-
- Supplying a new `transform` and a new `queries` argument will properly overwrite the existing base properties.
24+
:::caution
25+
`.extend` will not merge two separate `useQueries` properties. For example, you cannot _just_ inherit the deferredQueries, you must either inherit all or none of the `useQueries` argument.
26+
:::
27+
:::tip Reusable transformers
28+
You can extend as many times as you'd like. You can use this feature to easily inject reusable snippets, like transformers.
29+
30+
```typescript
31+
type QueryRecord = Record<string, UseQueryResult<unknown>>;
32+
33+
export const transformData = {
34+
transform: (data: {
35+
queries: QueryRecord;
36+
deferredQueries: QueryRecord;
37+
payload: unknown;
38+
}) => {
39+
// handle transformation in generic way
40+
},
41+
};
42+
```
2443

25-
All other properties in the loader will overwrite as expected. You can, for example, just supply a new `onLoading`, or `onError`.
44+
```typescript
45+
const loader = createLoader({...}).extend(transformData);
46+
```
2647

27-
:::info
28-
You may extend _extended_ loaders, to create an inheritance model.
2948
:::

docs/docs/Features/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ sidebar_position: 5
77
RTK Query Loader tries to give you all the flexibility you need to create reusable and extendable loaders.
88

99
- Supply multiple queries.
10-
- Supply queries that are [not from RTK](../Exports/use-create-query.md).
1110
- Supply queries that [don't affect loading state](defer-queries.md).
1211
- [Transform](./transforming.md) the queries to your desired output-format.
1312
- [Extend](./extending.md) existing loaders.
1413
- Re-use existing loaders
14+
15+
And even if you don't use `RTK Query`...
16+
17+
- Supply queries that are [just promises](../Exports/use-create-query.md).
18+
- [Use with other data loading libraries](./other-libs.md)

docs/docs/Features/other-libs.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
sidebar_position: 4
3+
---
4+
5+
# Use with other libraries
6+
7+
You can use RTK Query Loader with most other similar query-fetching libraries. This is possible through the use of _resolvers_.
8+
9+
:::note
10+
Although `rtk-query-loader` was build with `@reduxjs/toolkit` in mind, the underlying principles can be applied to any similar data loading solution. [Read more about how RTK Query Loader works under the hood](../Advanced/under-the-hood.md).
11+
:::
12+
13+
## Tanstack Query
14+
15+
```typescript
16+
import {
17+
useQuery,
18+
UseQueryResult as TanstackUseQueryResult,
19+
UseQueryOptions,
20+
} from "@tanstack/react-query";
21+
22+
const tanstackResolver = <T extends unknown>(
23+
query: TanstackUseQueryResult<T>
24+
): UseQueryResult<T> & {
25+
orignal_query: TanstackUseQueryResult<T>;
26+
} => ({
27+
isLoading: query.isLoading,
28+
isFetching: query.isFetching,
29+
isSuccess: query.isSuccess,
30+
isError: query.isError,
31+
error: query.error,
32+
data: query.data,
33+
isUninitialized: !query.isFetchedAfterMount,
34+
originalArgs: null,
35+
refetch: () => query.refetch(),
36+
currentData: query.data,
37+
endpointName: undefined,
38+
original_query: query,
39+
});
40+
41+
const useTanstackQuery = <T extends unknown>(
42+
args: UseQueryOptions<T>
43+
) => tanstackResolver(useQuery(args));
44+
```
45+
46+
This is how you would use it:
47+
48+
```typescript
49+
import { useTanstackQuery } from "../loader-resolvers";
50+
51+
const loader = createLoader({
52+
useQueries: () => {
53+
const repoData = useTanstackQuery({
54+
queryKey: ["repoData"],
55+
queryFn: () =>
56+
fetch(
57+
"https://api.github.com/repos/ryfylke-react-as/rtk-query-loader"
58+
).then((res) => res.json() as Promise<RepoDataResponse>),
59+
});
60+
61+
return {
62+
queries: {
63+
repoData,
64+
},
65+
};
66+
},
67+
});
68+
```
69+
70+
The output format will obviously be a bit different, but in this example you have access to the original query at the `.original_query` property.
71+
72+
## Other libraries
73+
74+
If you are interested in creating resolvers for other libraries, you can [edit this page](https://github.com/ryfylke-react-as/rtk-query-loader/tree/main/docs/docs/Advanced/other-libs.md) and then [submit a pull request](https://github.com/ryfylke-react-as/rtk-query-loader/compare) on GitHub to share your resolver here, as an npm package, or with the code embedded directly in the docs.

0 commit comments

Comments
 (0)