@modelence/react-query 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -85
- package/dist/index.d.ts +6 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/index.ts +6 -5
- package/typedoc.json +12 -0
- package/examples/usage.tsx +0 -249
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm i @modelence/react-query @tanstack/react-query
|
|
|
10
10
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
|
-
This package provides `
|
|
13
|
+
This package provides `modelenceQuery` and `modelenceMutation` factory functions that can be used with TanStack Query's native `useQuery` and `useMutation` hooks. This approach, recommended by TanStack, gives you direct access to TanStack Query's full API while providing Modelence-specific query configurations.
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
@@ -18,11 +18,11 @@ This package provides `getQueryOptions` and `getMutationOptions` factory functio
|
|
|
18
18
|
|
|
19
19
|
```tsx
|
|
20
20
|
import { useQuery } from '@tanstack/react-query';
|
|
21
|
-
import {
|
|
21
|
+
import { modelenceQuery } from '@modelence/react-query';
|
|
22
22
|
|
|
23
23
|
function TodoList() {
|
|
24
24
|
const { data, isPending, error } = useQuery(
|
|
25
|
-
|
|
25
|
+
modelenceQuery('todo.getAll', { limit: 10 })
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
if (isPending) return <div>Loading...</div>;
|
|
@@ -42,13 +42,13 @@ function TodoList() {
|
|
|
42
42
|
|
|
43
43
|
```tsx
|
|
44
44
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
45
|
-
import {
|
|
45
|
+
import { modelenceMutation } from '@modelence/react-query';
|
|
46
46
|
|
|
47
47
|
function CreateTodo() {
|
|
48
48
|
const queryClient = useQueryClient();
|
|
49
49
|
|
|
50
50
|
const { mutate: createTodo, isPending } = useMutation({
|
|
51
|
-
...
|
|
51
|
+
...modelenceMutation('todo.create'),
|
|
52
52
|
onSuccess: () => {
|
|
53
53
|
// Invalidate and refetch todos
|
|
54
54
|
queryClient.invalidateQueries({ queryKey: ['todo.getAll'] });
|
|
@@ -72,11 +72,11 @@ function CreateTodo() {
|
|
|
72
72
|
|
|
73
73
|
```tsx
|
|
74
74
|
import { useQuery } from '@tanstack/react-query';
|
|
75
|
-
import {
|
|
75
|
+
import { modelenceQuery } from '@modelence/react-query';
|
|
76
76
|
|
|
77
77
|
function TodoDetail({ id }: { id: string }) {
|
|
78
78
|
const { data: todo } = useQuery({
|
|
79
|
-
...
|
|
79
|
+
...modelenceQuery('todo.getById', { id }),
|
|
80
80
|
enabled: !!id, // Only run query if id exists
|
|
81
81
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
82
82
|
refetchOnWindowFocus: false,
|
|
@@ -90,11 +90,11 @@ function TodoDetail({ id }: { id: string }) {
|
|
|
90
90
|
|
|
91
91
|
```tsx
|
|
92
92
|
import { useMutation } from '@tanstack/react-query';
|
|
93
|
-
import {
|
|
93
|
+
import { modelenceMutation } from '@modelence/react-query';
|
|
94
94
|
|
|
95
95
|
function UpdateTodo({ todoId }: { todoId: string }) {
|
|
96
96
|
const { mutate: updateTodo } = useMutation({
|
|
97
|
-
...
|
|
97
|
+
...modelenceMutation('todo.update', { id: todoId }), // Default args
|
|
98
98
|
onSuccess: (data) => {
|
|
99
99
|
console.log('Todo updated:', data);
|
|
100
100
|
},
|
|
@@ -112,7 +112,7 @@ function UpdateTodo({ todoId }: { todoId: string }) {
|
|
|
112
112
|
|
|
113
113
|
```tsx
|
|
114
114
|
import { useQueryClient } from '@tanstack/react-query';
|
|
115
|
-
import { createQueryKey,
|
|
115
|
+
import { createQueryKey, modelenceQuery } from '@modelence/react-query';
|
|
116
116
|
|
|
117
117
|
function TodoActions() {
|
|
118
118
|
const queryClient = useQueryClient();
|
|
@@ -125,7 +125,7 @@ function TodoActions() {
|
|
|
125
125
|
|
|
126
126
|
const prefetchTodo = (id: string) => {
|
|
127
127
|
queryClient.prefetchQuery({
|
|
128
|
-
...
|
|
128
|
+
...modelenceQuery('todo.getById', { id }),
|
|
129
129
|
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
130
130
|
});
|
|
131
131
|
};
|
|
@@ -138,77 +138,3 @@ function TodoActions() {
|
|
|
138
138
|
);
|
|
139
139
|
}
|
|
140
140
|
```
|
|
141
|
-
|
|
142
|
-
## API Reference
|
|
143
|
-
|
|
144
|
-
### `getQueryOptions<T>(methodName, args?)`
|
|
145
|
-
|
|
146
|
-
Creates a query configuration object for use with TanStack Query's `useQuery`.
|
|
147
|
-
|
|
148
|
-
**Parameters:**
|
|
149
|
-
- `methodName` (string): The Modelence method name (e.g., 'todo.getAll')
|
|
150
|
-
- `args` (object, optional): Arguments to pass to the method
|
|
151
|
-
|
|
152
|
-
**Returns:** Query configuration object with `queryKey` and `queryFn`
|
|
153
|
-
|
|
154
|
-
### `getMutationOptions<T, TVariables>(methodName, defaultArgs?)`
|
|
155
|
-
|
|
156
|
-
Creates a mutation configuration object for use with TanStack Query's `useMutation`.
|
|
157
|
-
|
|
158
|
-
**Parameters:**
|
|
159
|
-
- `methodName` (string): The Modelence method name (e.g., 'todo.create')
|
|
160
|
-
- `defaultArgs` (object, optional): Default arguments merged with mutation variables
|
|
161
|
-
|
|
162
|
-
**Returns:** Mutation configuration object with `mutationFn`
|
|
163
|
-
|
|
164
|
-
### `createQueryKey<T, U>(methodName, args?)`
|
|
165
|
-
|
|
166
|
-
Utility function to create typed query keys for manual cache operations.
|
|
167
|
-
|
|
168
|
-
**Parameters:**
|
|
169
|
-
- `methodName` (T): The method name
|
|
170
|
-
- `args` (U, optional): The arguments
|
|
171
|
-
|
|
172
|
-
**Returns:** Typed query key array
|
|
173
|
-
|
|
174
|
-
## Migration from Modelence's useQuery/useMutation
|
|
175
|
-
|
|
176
|
-
### Before
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
import { useQuery, useMutation } from 'modelence/client';
|
|
180
|
-
|
|
181
|
-
function TodoComponent() {
|
|
182
|
-
const { data, isFetching, error } = useQuery('todo.getAll');
|
|
183
|
-
const { mutate: createTodo } = useMutation('todo.create');
|
|
184
|
-
|
|
185
|
-
// ...
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### After
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
193
|
-
import { getQueryOptions, getMutationOptions } from '@modelence/react-query';
|
|
194
|
-
|
|
195
|
-
function TodoComponent() {
|
|
196
|
-
const { data, isPending: isFetching, error } = useQuery(
|
|
197
|
-
getQueryOptions('todo.getAll')
|
|
198
|
-
);
|
|
199
|
-
const { mutate: createTodo } = useMutation(
|
|
200
|
-
getMutationOptions('todo.create')
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
// ...
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## Benefits
|
|
208
|
-
|
|
209
|
-
1. **Full TanStack Query API**: Access to all TanStack Query features and options
|
|
210
|
-
2. **Simple and Explicit**: Clear separation between Modelence configuration and TanStack Query options
|
|
211
|
-
3. **Better TypeScript Support**: Improved type inference and safety
|
|
212
|
-
4. **Familiar API**: Standard TanStack Query patterns that developers already know
|
|
213
|
-
5. **Future-Proof**: Easy to adopt new TanStack Query features as they're released
|
|
214
|
-
6. **Composability**: Easy to combine with other TanStack Query utilities
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,6 @@ type Args = Record<string, unknown>;
|
|
|
2
2
|
/**
|
|
3
3
|
* Creates query options for use with TanStack Query's useQuery hook.
|
|
4
4
|
*
|
|
5
|
-
* @typeParam T - The expected return type of the query
|
|
6
|
-
* @param methodName - The name of the method to query
|
|
7
|
-
* @param args - Optional arguments to pass to the method
|
|
8
|
-
* @returns Query options object for TanStack Query's useQuery
|
|
9
|
-
*
|
|
10
5
|
* @example
|
|
11
6
|
* ```tsx
|
|
12
7
|
* import { useQuery } from '@tanstack/react-query';
|
|
@@ -26,6 +21,12 @@ type Args = Record<string, unknown>;
|
|
|
26
21
|
* return <div>{data?.name}</div>;
|
|
27
22
|
* }
|
|
28
23
|
* ```
|
|
24
|
+
*
|
|
25
|
+
* @typeParam T - The expected return type of the query
|
|
26
|
+
* @param methodName - The name of the method to query
|
|
27
|
+
* @param args - Optional arguments to pass to the method
|
|
28
|
+
* @returns Query options object for TanStack Query's useQuery
|
|
29
|
+
*
|
|
29
30
|
*/
|
|
30
31
|
declare function modelenceQuery<T = unknown>(methodName: string, args?: Args): {
|
|
31
32
|
queryKey: (string | Args)[];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["modelenceQuery","methodName","args","callMethod","modelenceMutation","defaultArgs","variables","createQueryKey"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["modelenceQuery","methodName","args","callMethod","modelenceMutation","defaultArgs","variables","createQueryKey"],"mappings":"0CAiCO,SAASA,CACdC,CAAAA,CAAAA,CACAC,CAAa,CAAA,EACb,CAAA,CACA,OAAO,CACL,QAAU,CAAA,CAACD,CAAYC,CAAAA,CAAI,CAC3B,CAAA,OAAA,CAAS,IAAMC,UAAAA,CAAcF,CAAYC,CAAAA,CAAI,CAC/C,CACF,CAiCO,SAASE,CACdH,CAAAA,CAAAA,CACAI,CAAoB,CAAA,EACpB,CAAA,CACA,OAAO,CACL,UAAY,CAAA,CAACC,CAAkB,CAAA,EAAOH,GAAAA,UAAAA,CAAcF,CAAY,CAAA,CAAE,GAAGI,CAAAA,CAAa,GAAGC,CAAU,CAAC,CAClG,CACF,CA8BO,SAASC,CAAAA,CACdN,CACAC,CAAAA,CAAAA,CAAU,EAAC,CACc,CACzB,OAAO,CAACD,CAAAA,CAAYC,CAAI,CAC1B","file":"index.js","sourcesContent":["import { callMethod } from 'modelence/client';\n\ntype Args = Record<string, unknown>;\n\n/**\n * Creates query options for use with TanStack Query's useQuery hook.\n * \n * @example\n * ```tsx\n * import { useQuery } from '@tanstack/react-query';\n * import { modelenceQuery } from '@modelence/react-query';\n * \n * function MyComponent() {\n * // Basic usage\n * const { data } = useQuery(modelenceQuery('todo.getAll'));\n * \n * // With additional options\n * const { data: todo } = useQuery({\n * ...modelenceQuery('todo.getById', { id: '123' }),\n * enabled: !!id,\n * staleTime: 5 * 60 * 1000,\n * });\n * \n * return <div>{data?.name}</div>;\n * }\n * ```\n * \n * @typeParam T - The expected return type of the query\n * @param methodName - The name of the method to query\n * @param args - Optional arguments to pass to the method\n * @returns Query options object for TanStack Query's useQuery\n * \n */\nexport function modelenceQuery<T = unknown>(\n methodName: string, \n args: Args = {}\n) {\n return {\n queryKey: [methodName, args],\n queryFn: () => callMethod<T>(methodName, args),\n };\n}\n\n/**\n * Creates mutation options for use with TanStack Query's useMutation hook.\n * \n * @typeParam T - The expected return type of the mutation\n * @param methodName - The name of the method to mutate\n * @param defaultArgs - Optional default arguments to merge with mutation variables\n * @returns Mutation options object for TanStack Query's useMutation\n * \n * @example\n * ```tsx\n * import { useMutation, useQueryClient } from '@tanstack/react-query';\n * import { modelenceMutation } from '@modelence/react-query';\n * \n * function MyComponent() {\n * const queryClient = useQueryClient();\n * \n * // Basic usage\n * const { mutate } = useMutation(modelenceMutation('todos.create'));\n * \n * // With additional options\n * const { mutate: updateTodo } = useMutation({\n * ...modelenceMutation('todos.update'),\n * onSuccess: () => {\n * queryClient.invalidateQueries({ queryKey: ['todos.getAll'] });\n * },\n * });\n * \n * return <button onClick={() => mutate({ title: 'New Todo' })}>Create</button>;\n * }\n * ```\n */\nexport function modelenceMutation<T = unknown>(\n methodName: string, \n defaultArgs: Args = {}\n) {\n return {\n mutationFn: (variables: Args = {}) => callMethod<T>(methodName, { ...defaultArgs, ...variables }),\n };\n}\n\n/**\n * Type helper for creating properly typed query keys\n */\nexport type ModelenceQueryKey<T extends string, U extends Args = Args> = readonly [T, U];\n\n/**\n * Utility function to create query keys for manual cache operations\n * \n * @param methodName - The method name\n * @param args - The arguments\n * @returns Typed query key\n * \n * @example\n * ```tsx\n * import { useQueryClient } from '@tanstack/react-query';\n * import { createQueryKey } from '@modelence/react-query';\n * \n * function TodoActions() {\n * const queryClient = useQueryClient();\n * \n * const refreshTodos = () => {\n * queryClient.invalidateQueries({ \n * queryKey: createQueryKey('todo.getAll', { limit: 10 }) \n * });\n * };\n * }\n * ```\n */\nexport function createQueryKey<T extends string, U extends Args = Args>(\n methodName: T,\n args: U = {} as U\n): ModelenceQueryKey<T, U> {\n return [methodName, args] as const;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@modelence/react-query",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "React Query utilities for Modelence",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -19,13 +19,12 @@
|
|
|
19
19
|
"author": "Modelence",
|
|
20
20
|
"license": "SEE LICENSE IN LICENSE",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
+
"modelence": "*",
|
|
22
23
|
"@tanstack/react-query": ">=5.0.0",
|
|
23
24
|
"react": ">=18.0.0"
|
|
24
25
|
},
|
|
25
|
-
"dependencies": {
|
|
26
|
-
"modelence": "^0.5.0"
|
|
27
|
-
},
|
|
28
26
|
"devDependencies": {
|
|
27
|
+
"modelence": "^0.5.0",
|
|
29
28
|
"@tanstack/react-query": "^5.76.2",
|
|
30
29
|
"@types/react": "^19.0.0",
|
|
31
30
|
"react": "^19.0.0",
|
package/src/index.ts
CHANGED
|
@@ -5,11 +5,6 @@ type Args = Record<string, unknown>;
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates query options for use with TanStack Query's useQuery hook.
|
|
7
7
|
*
|
|
8
|
-
* @typeParam T - The expected return type of the query
|
|
9
|
-
* @param methodName - The name of the method to query
|
|
10
|
-
* @param args - Optional arguments to pass to the method
|
|
11
|
-
* @returns Query options object for TanStack Query's useQuery
|
|
12
|
-
*
|
|
13
8
|
* @example
|
|
14
9
|
* ```tsx
|
|
15
10
|
* import { useQuery } from '@tanstack/react-query';
|
|
@@ -29,6 +24,12 @@ type Args = Record<string, unknown>;
|
|
|
29
24
|
* return <div>{data?.name}</div>;
|
|
30
25
|
* }
|
|
31
26
|
* ```
|
|
27
|
+
*
|
|
28
|
+
* @typeParam T - The expected return type of the query
|
|
29
|
+
* @param methodName - The name of the method to query
|
|
30
|
+
* @param args - Optional arguments to pass to the method
|
|
31
|
+
* @returns Query options object for TanStack Query's useQuery
|
|
32
|
+
*
|
|
32
33
|
*/
|
|
33
34
|
export function modelenceQuery<T = unknown>(
|
|
34
35
|
methodName: string,
|
package/typedoc.json
ADDED
package/examples/usage.tsx
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
-
import { getQueryOptions, getMutationOptions, createQueryKey } from '@modelence/react-query';
|
|
4
|
-
|
|
5
|
-
interface Todo {
|
|
6
|
-
id: string;
|
|
7
|
-
title: string;
|
|
8
|
-
completed: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Example 1: Basic query usage
|
|
12
|
-
function TodoList() {
|
|
13
|
-
const { data: todos, isPending, error } = useQuery<Todo[]>(
|
|
14
|
-
getQueryOptions('todo.getAll', { limit: 10 })
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
if (isPending) return <div>Loading todos...</div>;
|
|
18
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div>
|
|
22
|
-
<h2>Todos</h2>
|
|
23
|
-
{todos?.map((todo) => (
|
|
24
|
-
<div key={todo.id}>
|
|
25
|
-
<h3>{todo.title}</h3>
|
|
26
|
-
<p>{todo.completed ? '✅' : '⏳'}</p>
|
|
27
|
-
</div>
|
|
28
|
-
))}
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Example 2: Query with options and enabled condition
|
|
34
|
-
function TodoDetail({ todoId }: { todoId: string | null }) {
|
|
35
|
-
const { data: todo, isPending } = useQuery<Todo>({
|
|
36
|
-
...getQueryOptions('todo.getById', { id: todoId }),
|
|
37
|
-
enabled: !!todoId, // Only run when todoId exists
|
|
38
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
39
|
-
retry: 3,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!todoId) return <div>Select a todo</div>;
|
|
43
|
-
if (isPending) return <div>Loading todo...</div>;
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div>
|
|
47
|
-
<h3>{todo?.title}</h3>
|
|
48
|
-
<p>Status: {todo?.completed ? 'Completed' : 'Pending'}</p>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Example 3: Basic mutation
|
|
54
|
-
function CreateTodo() {
|
|
55
|
-
const queryClient = useQueryClient();
|
|
56
|
-
|
|
57
|
-
const { mutate: createTodo, isPending, error } = useMutation<Todo, Error, { title: string; completed: boolean }>({
|
|
58
|
-
...getMutationOptions('todo.create'),
|
|
59
|
-
onSuccess: () => {
|
|
60
|
-
// Invalidate and refetch all todo queries
|
|
61
|
-
queryClient.invalidateQueries({ queryKey: ['todo.getAll'] });
|
|
62
|
-
},
|
|
63
|
-
onError: (error) => {
|
|
64
|
-
console.error('Failed to create todo:', error);
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
const formData = new FormData(e.currentTarget);
|
|
71
|
-
const title = formData.get('title') as string;
|
|
72
|
-
|
|
73
|
-
createTodo({ title, completed: false });
|
|
74
|
-
e.currentTarget.reset();
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<form onSubmit={handleSubmit}>
|
|
79
|
-
<input
|
|
80
|
-
name="title"
|
|
81
|
-
placeholder="Enter todo title"
|
|
82
|
-
required
|
|
83
|
-
disabled={isPending}
|
|
84
|
-
/>
|
|
85
|
-
<button type="submit" disabled={isPending}>
|
|
86
|
-
{isPending ? 'Creating...' : 'Create Todo'}
|
|
87
|
-
</button>
|
|
88
|
-
{error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
|
|
89
|
-
</form>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Example 4: Mutation with default args
|
|
94
|
-
function UpdateTodo({ todoId }: { todoId: string }) {
|
|
95
|
-
const queryClient = useQueryClient();
|
|
96
|
-
|
|
97
|
-
const { mutate: updateTodo, isPending } = useMutation<Todo, Error, { completed: boolean }>({
|
|
98
|
-
...getMutationOptions('todo.update', { id: todoId }), // Default id
|
|
99
|
-
onSuccess: () => {
|
|
100
|
-
// Invalidate specific todo and list
|
|
101
|
-
queryClient.invalidateQueries({ queryKey: ['todo.getById'] });
|
|
102
|
-
queryClient.invalidateQueries({ queryKey: ['todo.getAll'] });
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const toggleComplete = () => {
|
|
107
|
-
// The id is already provided in defaultArgs, so we only need the fields to update
|
|
108
|
-
updateTodo({ completed: true });
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<button onClick={toggleComplete} disabled={isPending}>
|
|
113
|
-
{isPending ? 'Updating...' : 'Mark Complete'}
|
|
114
|
-
</button>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Example 5: Manual cache operations
|
|
119
|
-
function TodoActions() {
|
|
120
|
-
const queryClient = useQueryClient();
|
|
121
|
-
|
|
122
|
-
const refreshTodos = () => {
|
|
123
|
-
queryClient.invalidateQueries({
|
|
124
|
-
queryKey: createQueryKey('todo.getAll', { limit: 10 })
|
|
125
|
-
});
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const prefetchTodo = (id: string) => {
|
|
129
|
-
queryClient.prefetchQuery<Todo>({
|
|
130
|
-
...getQueryOptions('todo.getById', { id }),
|
|
131
|
-
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const setTodoData = (id: string, todo: Todo) => {
|
|
136
|
-
queryClient.setQueryData(
|
|
137
|
-
createQueryKey('todo.getById', { id }),
|
|
138
|
-
todo
|
|
139
|
-
);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
<div>
|
|
144
|
-
<button onClick={refreshTodos}>Refresh Todos</button>
|
|
145
|
-
<button onClick={() => prefetchTodo('123')}>Prefetch Todo 123</button>
|
|
146
|
-
<button onClick={() => setTodoData('123', { id: '123', title: 'Test', completed: false })}>
|
|
147
|
-
Set Todo Data
|
|
148
|
-
</button>
|
|
149
|
-
</div>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Example 6: Advanced usage with optimistic updates
|
|
154
|
-
function OptimisticTodo({ todoId }: { todoId: string }) {
|
|
155
|
-
const queryClient = useQueryClient();
|
|
156
|
-
|
|
157
|
-
const { mutate: updateTodo } = useMutation<
|
|
158
|
-
Todo,
|
|
159
|
-
Error,
|
|
160
|
-
{ id: string; completed: boolean },
|
|
161
|
-
{ previousTodo: Todo | undefined }
|
|
162
|
-
>({
|
|
163
|
-
...getMutationOptions('todo.update'),
|
|
164
|
-
onMutate: async (variables) => {
|
|
165
|
-
// Cancel outgoing refetches (so they don't overwrite our optimistic update)
|
|
166
|
-
await queryClient.cancelQueries({
|
|
167
|
-
queryKey: createQueryKey('todo.getById', { id: todoId })
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Snapshot the previous value
|
|
171
|
-
const previousTodo = queryClient.getQueryData<Todo>(
|
|
172
|
-
createQueryKey('todo.getById', { id: todoId })
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Optimistically update to the new value
|
|
176
|
-
queryClient.setQueryData(
|
|
177
|
-
createQueryKey('todo.getById', { id: todoId }),
|
|
178
|
-
(old: Todo | undefined) => old ? { ...old, ...variables } : undefined
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
// Return a context object with the snapshotted value
|
|
182
|
-
return { previousTodo };
|
|
183
|
-
},
|
|
184
|
-
onError: (_err, _variables, context) => {
|
|
185
|
-
// If the mutation fails, use the context to roll back
|
|
186
|
-
if (context?.previousTodo) {
|
|
187
|
-
queryClient.setQueryData(
|
|
188
|
-
createQueryKey('todo.getById', { id: todoId }),
|
|
189
|
-
context.previousTodo
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
onSettled: () => {
|
|
194
|
-
// Always refetch after error or success
|
|
195
|
-
queryClient.invalidateQueries({
|
|
196
|
-
queryKey: createQueryKey('todo.getById', { id: todoId })
|
|
197
|
-
});
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
return (
|
|
202
|
-
<button onClick={() => updateTodo({ id: todoId, completed: true })}>
|
|
203
|
-
Update with Optimistic UI
|
|
204
|
-
</button>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Main app component showcasing all examples
|
|
209
|
-
export default function App() {
|
|
210
|
-
const [selectedTodoId, setSelectedTodoId] = React.useState<string | null>(null);
|
|
211
|
-
|
|
212
|
-
return (
|
|
213
|
-
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
|
|
214
|
-
<h1>@modelence/react-query Examples</h1>
|
|
215
|
-
|
|
216
|
-
<section>
|
|
217
|
-
<h2>Create Todo</h2>
|
|
218
|
-
<CreateTodo />
|
|
219
|
-
</section>
|
|
220
|
-
|
|
221
|
-
<section>
|
|
222
|
-
<h2>Todo List</h2>
|
|
223
|
-
<TodoList />
|
|
224
|
-
</section>
|
|
225
|
-
|
|
226
|
-
<section>
|
|
227
|
-
<h2>Todo Detail</h2>
|
|
228
|
-
<input
|
|
229
|
-
placeholder="Enter todo ID"
|
|
230
|
-
onChange={(e) => setSelectedTodoId(e.target.value || null)}
|
|
231
|
-
/>
|
|
232
|
-
<TodoDetail todoId={selectedTodoId} />
|
|
233
|
-
</section>
|
|
234
|
-
|
|
235
|
-
{selectedTodoId && (
|
|
236
|
-
<section>
|
|
237
|
-
<h2>Todo Actions</h2>
|
|
238
|
-
<UpdateTodo todoId={selectedTodoId} />
|
|
239
|
-
<OptimisticTodo todoId={selectedTodoId} />
|
|
240
|
-
</section>
|
|
241
|
-
)}
|
|
242
|
-
|
|
243
|
-
<section>
|
|
244
|
-
<h2>Cache Actions</h2>
|
|
245
|
-
<TodoActions />
|
|
246
|
-
</section>
|
|
247
|
-
</div>
|
|
248
|
-
);
|
|
249
|
-
}
|