@hitch42/applet 0.1.1-beta.1 → 0.1.1-beta.11
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/dist/_internal/client.js +120 -11
- package/dist/_internal/log-context.d.ts +34 -0
- package/dist/_internal/log-context.js +2 -0
- package/dist/_internal/server.d.ts +14 -1
- package/dist/_internal/server.js +122 -118
- package/dist/applet.js +53 -29
- package/dist/build-Bh_S5BUv.js +2 -0
- package/dist/{build-Rk3xDZV7.js → build-CEfFoIZZ.js} +30 -14
- package/dist/ci-BKw14X2h.js +69 -0
- package/dist/client.d.ts +3 -3
- package/dist/{contract-DMrpbaLJ.d.ts → contract-CsUpTBe0.d.ts} +1 -1
- package/dist/{deploy-D3BGDqgC.js → deploy-wGYjwXrx.js} +249 -35
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/{generate-CH0VQmUi.js → generate-BEIYs17x.js} +79 -25
- package/dist/generate-Dox-OYt6.js +2 -0
- package/dist/headers-DkNr9MLJ.js +17 -0
- package/dist/index.d.ts +123 -11
- package/dist/index.js +142 -26
- package/dist/{init-BS1Y-eNL.js → init-6oGPM46R.js} +17 -18
- package/dist/load-config-xEdTXs0y.js +27 -0
- package/dist/log-context-zu8U9Sjm.js +73 -0
- package/dist/login-CUx3TmFQ.js +12 -0
- package/dist/{logs-m00pzr_O.js → logs-DSjuEN0Q.js} +20 -22
- package/dist/{manifest-BQCAxATS.d.ts → manifest-C6_F4bL8.d.ts} +9 -0
- package/dist/{manifest-DyoJ1MIL.js → manifest-CJSL9GuC.js} +25 -3
- package/dist/me-BW7Paary.js +32 -0
- package/dist/{project-CxN-Dtc7.js → project-BRZGd6xq.js} +227 -34
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/query-cache-CXmAyLv3.js +82 -0
- package/dist/react.d.ts +3 -2
- package/dist/react.js +98 -80
- package/dist/schedules-C8_Svr4h.js +42 -0
- package/dist/server.d.ts +9 -4
- package/dist/server.js +29 -8
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +97 -0
- package/dist/telephony.js +726 -0
- package/dist/{upload-YbRKEqR6.d.ts → upload-DKZzz5kW.d.ts} +6 -6
- package/dist/{variables-CP7Ce3Np.js → variables-DT5RyHdH.js} +14 -39
- package/dist/vite.d.ts +3 -60
- package/dist/vite.js +503 -34
- package/dist/workflow-engine-BOTzURKM.js +111 -0
- package/dist/{auth-DaVK0k5R.js → workspace-D0dnnlxL.js} +75 -40
- package/package.json +19 -6
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +60 -15
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +29 -2
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +2 -0
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
- package/scaffold/.agents/skills/hitch-applets/references/records.md +32 -8
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
- package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
- package/scaffold/.agents/skills/hitch-cli/SKILL.md +129 -0
- package/scaffold/.agents/skills/hitch-data/SKILL.md +52 -0
- package/scaffold/.agents/skills/hitch-data/references/attributes.md +127 -0
- package/scaffold/.agents/skills/hitch-data/references/limits.md +64 -0
- package/scaffold/.agents/skills/hitch-theory/SKILL.md +151 -0
- package/scaffold/.agents/skills/react-best-practices/SKILL.md +127 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-effect-event-deps.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-defer-await.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-dependencies.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/async-parallel.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-analyzable-paths.md +42 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-barrel-imports.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-conditional.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-defer-third-party.md +46 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-dynamic-imports.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/bundle-preload.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-event-listeners.md +78 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
- package/scaffold/.agents/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-function-results.md +58 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-cache-storage.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-flatmap-filter.md +51 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-length-check-first.md +50 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-request-idle-callback.md +104 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/scaffold/.agents/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-resource-hints.md +81 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-script-defer-async.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rendering-usetransition-loading.md +43 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-no-inline-components.md +75 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/scaffold/.agents/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/scaffold/.agents/skills/tanstack-query/SKILL.md +136 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/automatic-invalidation-after-mutations.md +124 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/broadcast-realtime-and-multi-tab-synchronization.md +150 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/compose-query-with-tanstack-router-and-start.md +248 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/enforce-query-best-practices-with-eslint.md +167 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/persist-offline-and-restore-caches.md +181 -0
- package/scaffold/.agents/skills/tanstack-query/references/compositions/query-data-and-forms.md +156 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/build-query-abstractions.md +130 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/cancel-queries-and-consume-abort-signals.md +176 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/concurrent-optimistic-updates.md +135 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/coordinate-query-execution.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/design-query-keys-and-options.md +182 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/fetch-and-observe-queries.md +193 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/implement-optimistic-updates-and-cache-writes.md +168 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/paginate-and-build-infinite-queries.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/seed-placeholder-select-and-transform-data.md +188 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/selectors-and-derived-state.md +145 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/tune-defaults-freshness-retries-and-refetching.md +172 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/understand-query-internals-and-observers.md +104 -0
- package/scaffold/.agents/skills/tanstack-query/references/core/write-mutations-and-invalidate-related-queries.md +183 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/debug-with-devtools.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/handle-status-and-errors.md +119 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/shape-data-and-render-efficiently.md +184 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-framework-adapter-reactivity.md +185 -0
- package/scaffold/.agents/skills/tanstack-query/references/framework/use-suspense-and-error-boundaries.md +208 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/migrate-major-versions-and-codemods.md +169 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/prefetch-and-remove-request-waterfalls.md +228 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/setup-query-client-and-providers.md +161 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/ssr-hydration-and-streaming.md +209 -0
- package/scaffold/.agents/skills/tanstack-query/references/lifecycle/test-query-code.md +160 -0
- package/scaffold/.agents/skills/tanstack-router/SKILL.md +134 -0
- package/scaffold/.agents/skills/tanstack-router/references/compositions/router-query.md +412 -0
- package/scaffold/.agents/skills/tanstack-router/references/react-router.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/auth-and-guards.md +495 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/code-splitting.md +318 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/data-loading.md +490 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/index.md +158 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/navigation.md +433 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/not-found-and-errors.md +436 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/path-params.md +383 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/search-params.md +343 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/ssr.md +462 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-core/type-safety.md +493 -0
- package/scaffold/.agents/skills/tanstack-router/references/router-plugin.md +250 -0
- package/scaffold/.agents/skills/tanstack-router/references/virtual-file-routes.md +215 -0
- package/scaffold/.agents/skills/web-design-guidelines/SKILL.md +184 -0
- package/scaffold/.github/workflows/hitch.yml +15 -0
- package/scaffold/AGENTS.md +2 -2
- package/scaffold/applet/src/client/components/ui/alert.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/button.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/checkbox.tsx +1 -1
- package/scaffold/applet/src/client/components/ui/dialog.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/input-group.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/select.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/sheet.tsx +2 -2
- package/scaffold/applet/src/client/components/ui/spinner.tsx +2 -1
- package/scaffold/applet/src/client/components/ui/tabs.tsx +1 -1
- package/scaffold/applet/src/client/lib/utils.ts +1 -6
- package/scaffold/applet/src/client/styles/globals.css +2 -0
- package/dist/build-CEku1lyb.js +0 -2
- package/dist/generate-6Nbt7uii.js +0 -2
- package/dist/load-config-BnxocLAQ.js +0 -56
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: core/implement-optimistic-updates-and-cache-writes
|
|
3
|
+
description: >
|
|
4
|
+
Use this when implementing optimistic updates, onMutate rollback context,
|
|
5
|
+
cancelQueries before writes, setQueryData, setQueriesData, immutable cache writes,
|
|
6
|
+
mutation rollback, and offline-aware optimistic state.
|
|
7
|
+
type: core
|
|
8
|
+
library: TanStack Query
|
|
9
|
+
library_version: "5.101.0"
|
|
10
|
+
requires:
|
|
11
|
+
- core/write-mutations-and-invalidate-related-queries
|
|
12
|
+
- core/cancel-queries-and-consume-abort-signals
|
|
13
|
+
sources:
|
|
14
|
+
- TanStack/query:docs/framework/react/guides/optimistic-updates.md
|
|
15
|
+
- TanStack/query:docs/framework/react/guides/updates-from-mutation-responses.md
|
|
16
|
+
- TanStack/query:docs/framework/react/guides/query-cancellation.md
|
|
17
|
+
- TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
24
|
+
|
|
25
|
+
type Todo = { id: number; title: string };
|
|
26
|
+
|
|
27
|
+
export function useOptimisticAddTodo() {
|
|
28
|
+
const queryClient = useQueryClient();
|
|
29
|
+
return useMutation({
|
|
30
|
+
mutationFn: async (title: string) => ({ id: Date.now(), title }),
|
|
31
|
+
onMutate: async (title) => {
|
|
32
|
+
await queryClient.cancelQueries({ queryKey: ["todos"] });
|
|
33
|
+
const previous = queryClient.getQueryData<Array<Todo>>(["todos"]);
|
|
34
|
+
queryClient.setQueryData<Array<Todo>>(["todos"], (old = []) => [...old, { id: -1, title }]);
|
|
35
|
+
return { previous };
|
|
36
|
+
},
|
|
37
|
+
onError: (_error, _title, context) => {
|
|
38
|
+
queryClient.setQueryData(["todos"], context?.previous);
|
|
39
|
+
},
|
|
40
|
+
onSettled: async () => {
|
|
41
|
+
await queryClient.invalidateQueries({ queryKey: ["todos"] });
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Core Patterns
|
|
48
|
+
|
|
49
|
+
### Write mutation response into detail cache
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
53
|
+
|
|
54
|
+
export function useUpdateTodo() {
|
|
55
|
+
const queryClient = useQueryClient();
|
|
56
|
+
return useMutation({
|
|
57
|
+
mutationFn: async (todo: { id: number; title: string }) => todo,
|
|
58
|
+
onSuccess: (todo) => queryClient.setQueryData(["todo", todo.id], todo),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Update lists immutably
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
67
|
+
|
|
68
|
+
const queryClient = new QueryClient();
|
|
69
|
+
|
|
70
|
+
queryClient.setQueryData<Array<{ id: number; title: string }>>(["todos"], (old = []) =>
|
|
71
|
+
old.map((todo) => (todo.id === 1 ? { ...todo, title: "Done" } : todo)),
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Roll back with context
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
79
|
+
|
|
80
|
+
const queryClient = new QueryClient();
|
|
81
|
+
const previous = queryClient.getQueryData(["todos"]);
|
|
82
|
+
queryClient.setQueryData(["todos"], [{ id: 1, title: "Optimistic" }]);
|
|
83
|
+
queryClient.setQueryData(["todos"], previous);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Common Mistakes
|
|
87
|
+
|
|
88
|
+
### CRITICAL Optimistic write without cancellation
|
|
89
|
+
|
|
90
|
+
Wrong:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
94
|
+
|
|
95
|
+
const queryClient = new QueryClient();
|
|
96
|
+
queryClient.setQueryData(["todos"], [{ id: 1, title: "Optimistic" }]);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Correct:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
103
|
+
|
|
104
|
+
const queryClient = new QueryClient();
|
|
105
|
+
await queryClient.cancelQueries({ queryKey: ["todos"] });
|
|
106
|
+
queryClient.setQueryData(["todos"], [{ id: 1, title: "Optimistic" }]);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
An in-flight refetch can overwrite an optimistic write unless it is cancelled first.
|
|
110
|
+
|
|
111
|
+
Source: TanStack/query:docs/framework/react/guides/optimistic-updates.md
|
|
112
|
+
|
|
113
|
+
### CRITICAL In-place mutation
|
|
114
|
+
|
|
115
|
+
Wrong:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
119
|
+
|
|
120
|
+
const queryClient = new QueryClient();
|
|
121
|
+
queryClient.setQueryData<Array<{ id: number; done: boolean }>>(["todos"], (old = []) => {
|
|
122
|
+
old[0].done = true;
|
|
123
|
+
return old;
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Correct:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
131
|
+
|
|
132
|
+
const queryClient = new QueryClient();
|
|
133
|
+
queryClient.setQueryData<Array<{ id: number; done: boolean }>>(["todos"], (old = []) =>
|
|
134
|
+
old.map((todo) => (todo.id === 1 ? { ...todo, done: true } : todo)),
|
|
135
|
+
);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Cache updates must be immutable so observers can detect and share changes correctly.
|
|
139
|
+
|
|
140
|
+
Source: TanStack/query:docs/framework/react/guides/updates-from-mutation-responses.md
|
|
141
|
+
|
|
142
|
+
### HIGH Persisted persister loses optimistic mutation
|
|
143
|
+
|
|
144
|
+
Wrong:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
148
|
+
|
|
149
|
+
export const queryClient = new QueryClient();
|
|
150
|
+
queryClient.setMutationDefaults(["updateTodo"], {});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Correct:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
157
|
+
|
|
158
|
+
export const queryClient = new QueryClient();
|
|
159
|
+
queryClient.setMutationDefaults(["updateTodo"], {
|
|
160
|
+
mutationFn: async (todo: { id: number; title: string }) => todo,
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Paused persisted mutations need a default mutationFn after hydration because functions cannot be serialized.
|
|
165
|
+
|
|
166
|
+
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
167
|
+
|
|
168
|
+
See also: `core/cancel-queries-and-consume-abort-signals` before optimistic writes.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: core/paginate-and-build-infinite-queries
|
|
3
|
+
description: >
|
|
4
|
+
Use this when implementing pagination, lagged queries, placeholderData,
|
|
5
|
+
keepPreviousData migration, useInfiniteQuery, initialPageParam,
|
|
6
|
+
getNextPageParam, getPreviousPageParam, maxPages, pages, or pageParams.
|
|
7
|
+
type: core
|
|
8
|
+
library: TanStack Query
|
|
9
|
+
library_version: "5.101.0"
|
|
10
|
+
requires:
|
|
11
|
+
- core/design-query-keys-and-options
|
|
12
|
+
- core/fetch-and-observe-queries
|
|
13
|
+
sources:
|
|
14
|
+
- TanStack/query:docs/framework/react/guides/paginated-queries.md
|
|
15
|
+
- TanStack/query:docs/framework/react/guides/infinite-queries.md
|
|
16
|
+
- TanStack/query:docs/framework/react/reference/useInfiniteQuery.md
|
|
17
|
+
- TanStack/query:docs/reference/InfiniteQueryObserver.md
|
|
18
|
+
- TanStack/query:docs/eslint/infinite-query-property-order.md
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
25
|
+
|
|
26
|
+
export function useProjects(page: number) {
|
|
27
|
+
return useQuery({
|
|
28
|
+
queryKey: ["projects", page],
|
|
29
|
+
queryFn: async () => ({ page, projects: [{ id: page }] }),
|
|
30
|
+
placeholderData: keepPreviousData,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Core Patterns
|
|
36
|
+
|
|
37
|
+
### Build an infinite query
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
41
|
+
|
|
42
|
+
export function useFeed() {
|
|
43
|
+
return useInfiniteQuery({
|
|
44
|
+
queryKey: ["feed"],
|
|
45
|
+
queryFn: async ({ pageParam }) => ({
|
|
46
|
+
nextCursor: pageParam + 1,
|
|
47
|
+
items: [{ id: pageParam }],
|
|
48
|
+
}),
|
|
49
|
+
initialPageParam: 0,
|
|
50
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Bound memory with maxPages
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
59
|
+
|
|
60
|
+
export function useBoundedFeed() {
|
|
61
|
+
return useInfiniteQuery({
|
|
62
|
+
queryKey: ["feed"],
|
|
63
|
+
queryFn: async ({ pageParam }) => ({
|
|
64
|
+
nextCursor: pageParam + 1,
|
|
65
|
+
items: [{ id: pageParam }],
|
|
66
|
+
}),
|
|
67
|
+
initialPageParam: 0,
|
|
68
|
+
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
69
|
+
maxPages: 5,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Preserve infinite data shape
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
78
|
+
|
|
79
|
+
const queryClient = new QueryClient();
|
|
80
|
+
|
|
81
|
+
queryClient.setQueryData(["feed"], {
|
|
82
|
+
pages: [{ items: [{ id: 1 }], nextCursor: 2 }],
|
|
83
|
+
pageParams: [1],
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Common Mistakes
|
|
88
|
+
|
|
89
|
+
### CRITICAL Missing initialPageParam
|
|
90
|
+
|
|
91
|
+
Wrong:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
95
|
+
|
|
96
|
+
export function useFeed() {
|
|
97
|
+
return useInfiniteQuery({
|
|
98
|
+
queryKey: ["feed"],
|
|
99
|
+
queryFn: async () => ({ items: [] }),
|
|
100
|
+
getNextPageParam: () => 1,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Correct:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
109
|
+
|
|
110
|
+
export function useFeed() {
|
|
111
|
+
return useInfiniteQuery({
|
|
112
|
+
queryKey: ["feed"],
|
|
113
|
+
queryFn: async ({ pageParam }) => ({ items: [pageParam] }),
|
|
114
|
+
initialPageParam: 0,
|
|
115
|
+
getNextPageParam: () => 1,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
v5 requires an explicit initial page param so pageParams can be serialized and inferred.
|
|
121
|
+
|
|
122
|
+
Source: TanStack/query:docs/framework/react/guides/infinite-queries.md
|
|
123
|
+
|
|
124
|
+
### HIGH Overlapping infinite fetches
|
|
125
|
+
|
|
126
|
+
Wrong:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
130
|
+
|
|
131
|
+
export function Feed() {
|
|
132
|
+
const query = useInfiniteQuery({
|
|
133
|
+
queryKey: ["feed"],
|
|
134
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
135
|
+
initialPageParam: 0,
|
|
136
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
137
|
+
});
|
|
138
|
+
return <button onClick={() => query.fetchNextPage()}>More</button>;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Correct:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
146
|
+
|
|
147
|
+
export function Feed() {
|
|
148
|
+
const query = useInfiniteQuery({
|
|
149
|
+
queryKey: ["feed"],
|
|
150
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
151
|
+
initialPageParam: 0,
|
|
152
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
153
|
+
});
|
|
154
|
+
return (
|
|
155
|
+
<button disabled={query.isFetchingNextPage} onClick={() => query.fetchNextPage()}>
|
|
156
|
+
More
|
|
157
|
+
</button>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Only one ongoing fetch should update an infinite query cache at a time unless explicitly overridden.
|
|
163
|
+
|
|
164
|
+
Source: TanStack/query:docs/framework/react/guides/infinite-queries.md
|
|
165
|
+
|
|
166
|
+
### HIGH Broken pages shape
|
|
167
|
+
|
|
168
|
+
Wrong:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
172
|
+
|
|
173
|
+
const queryClient = new QueryClient();
|
|
174
|
+
queryClient.setQueryData(["feed"], [{ id: 1 }]);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Correct:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
181
|
+
|
|
182
|
+
const queryClient = new QueryClient();
|
|
183
|
+
queryClient.setQueryData(["feed"], { pages: [[{ id: 1 }]], pageParams: [0] });
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Infinite query data must keep `pages` and `pageParams`; refetches expect that shape.
|
|
187
|
+
|
|
188
|
+
Source: TanStack/query:docs/framework/react/guides/infinite-queries.md
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: core/seed-placeholder-select-and-transform-data
|
|
3
|
+
description: >
|
|
4
|
+
Use this when choosing initialData, initialDataUpdatedAt, placeholderData,
|
|
5
|
+
keepPreviousData, select, data transformation, cache seeding, detail-from-list
|
|
6
|
+
seeding, or derived query data.
|
|
7
|
+
type: core
|
|
8
|
+
library: TanStack Query
|
|
9
|
+
library_version: "5.101.0"
|
|
10
|
+
requires:
|
|
11
|
+
- core/design-query-keys-and-options
|
|
12
|
+
- core/fetch-and-observe-queries
|
|
13
|
+
sources:
|
|
14
|
+
- TanStack/query:docs/framework/react/guides/initial-query-data.md
|
|
15
|
+
- TanStack/query:docs/framework/react/guides/placeholder-query-data.md
|
|
16
|
+
- TanStack/query:docs/framework/react/guides/render-optimizations.md
|
|
17
|
+
- TanStack/query:docs/framework/react/reference/queryOptions.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
24
|
+
|
|
25
|
+
export function useProjects(page: number) {
|
|
26
|
+
return useQuery({
|
|
27
|
+
queryKey: ["projects", page],
|
|
28
|
+
queryFn: async () => ({ page, items: [{ id: page }] }),
|
|
29
|
+
placeholderData: keepPreviousData,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Core Patterns
|
|
35
|
+
|
|
36
|
+
### Seed detail data from a list
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { QueryClient, useQuery } from "@tanstack/react-query";
|
|
40
|
+
|
|
41
|
+
const queryClient = new QueryClient();
|
|
42
|
+
|
|
43
|
+
export function useTodo(todoId: number) {
|
|
44
|
+
return useQuery({
|
|
45
|
+
queryKey: ["todo", todoId],
|
|
46
|
+
queryFn: async () => ({ id: todoId, title: "Fresh" }),
|
|
47
|
+
initialData: () =>
|
|
48
|
+
queryClient
|
|
49
|
+
.getQueryData<Array<{ id: number; title: string }>>(["todos"])
|
|
50
|
+
?.find((todo) => todo.id === todoId),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Select the smallest shape
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { useQuery } from "@tanstack/react-query";
|
|
59
|
+
|
|
60
|
+
export function useTodoCount() {
|
|
61
|
+
return useQuery({
|
|
62
|
+
queryKey: ["todos"],
|
|
63
|
+
queryFn: async () => [{ id: 1 }, { id: 2 }],
|
|
64
|
+
select: (todos) => todos.length,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Transform in the query function for cache-wide shape
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { queryOptions } from "@tanstack/react-query";
|
|
73
|
+
|
|
74
|
+
export const todosOptions = queryOptions({
|
|
75
|
+
queryKey: ["todos"],
|
|
76
|
+
queryFn: async () =>
|
|
77
|
+
[{ id: 1, title: "ship" }].map((todo) => ({
|
|
78
|
+
...todo,
|
|
79
|
+
title: todo.title.toUpperCase(),
|
|
80
|
+
})),
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Common Mistakes
|
|
85
|
+
|
|
86
|
+
### HIGH initialData overwrite assumption
|
|
87
|
+
|
|
88
|
+
Wrong:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { useQuery } from "@tanstack/react-query";
|
|
92
|
+
|
|
93
|
+
export function useTodo() {
|
|
94
|
+
return useQuery({
|
|
95
|
+
queryKey: ["todo", 1],
|
|
96
|
+
queryFn: async () => ({ id: 1, title: "Server" }),
|
|
97
|
+
initialData: { id: 1, title: "Always" },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Correct:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { useQuery } from "@tanstack/react-query";
|
|
106
|
+
|
|
107
|
+
export function useTodo() {
|
|
108
|
+
return useQuery({
|
|
109
|
+
queryKey: ["todo", 1],
|
|
110
|
+
queryFn: async () => ({ id: 1, title: "Server" }),
|
|
111
|
+
placeholderData: { id: 1, title: "Temporary" },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`initialData` is persisted to cache as real data; placeholder data is observer-local.
|
|
117
|
+
|
|
118
|
+
Source: TanStack/query:docs/framework/react/guides/initial-query-data.md
|
|
119
|
+
|
|
120
|
+
### HIGH v4 keepPreviousData option
|
|
121
|
+
|
|
122
|
+
Wrong:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { useQuery } from "@tanstack/react-query";
|
|
126
|
+
|
|
127
|
+
export function usePage(page: number) {
|
|
128
|
+
return useQuery({
|
|
129
|
+
queryKey: ["page", page],
|
|
130
|
+
queryFn: async () => ({ page }),
|
|
131
|
+
keepPreviousData: true,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Correct:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
140
|
+
|
|
141
|
+
export function usePage(page: number) {
|
|
142
|
+
return useQuery({
|
|
143
|
+
queryKey: ["page", page],
|
|
144
|
+
queryFn: async () => ({ page }),
|
|
145
|
+
placeholderData: keepPreviousData,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
v5 replaced the `keepPreviousData` option with `placeholderData: keepPreviousData`.
|
|
151
|
+
|
|
152
|
+
Source: TanStack/query:docs/framework/react/guides/migrating-to-v5.md
|
|
153
|
+
|
|
154
|
+
### MEDIUM Throwing in select
|
|
155
|
+
|
|
156
|
+
Wrong:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { useQuery } from "@tanstack/react-query";
|
|
160
|
+
|
|
161
|
+
export function useFirstTodo() {
|
|
162
|
+
return useQuery({
|
|
163
|
+
queryKey: ["todos"],
|
|
164
|
+
queryFn: async () => [] as Array<{ id: number }>,
|
|
165
|
+
select: (todos) => todos[0].id,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Correct:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { useQuery } from "@tanstack/react-query";
|
|
174
|
+
|
|
175
|
+
export function useFirstTodo() {
|
|
176
|
+
return useQuery({
|
|
177
|
+
queryKey: ["todos"],
|
|
178
|
+
queryFn: async () => [] as Array<{ id: number }>,
|
|
179
|
+
select: (todos) => todos[0]?.id ?? null,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`select` transforms successful data; it is not the right place to model fetch errors.
|
|
185
|
+
|
|
186
|
+
Source: TanStack/query:docs/framework/react/guides/render-optimizations.md
|
|
187
|
+
|
|
188
|
+
See also: `lifecycle/ssr-hydration-and-streaming` for SSR tradeoffs compared to hydration.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: core/selectors-and-derived-state
|
|
3
|
+
description: >
|
|
4
|
+
Use this when selecting, transforming, or deriving data from TanStack Query:
|
|
5
|
+
select functions, selector memoization, fine-grained subscriptions, structural
|
|
6
|
+
sharing, queryOptions composition with select, and deriving client state from
|
|
7
|
+
server state instead of syncing it through effects.
|
|
8
|
+
type: core
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- core/seed-placeholder-select-and-transform-data
|
|
13
|
+
- framework/shape-data-and-render-efficiently
|
|
14
|
+
sources:
|
|
15
|
+
- TanStack/query:docs/framework/react/guides/render-optimizations.md
|
|
16
|
+
- TanStack/query:docs/framework/react/reference/queryOptions.md
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Core Patterns
|
|
20
|
+
|
|
21
|
+
Use `select` to subscribe a component to the data shape it actually needs while keeping the full response in the cache.
|
|
22
|
+
|
|
23
|
+
### Select a stable slice
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { queryOptions, useSuspenseQuery } from "@tanstack/react-query";
|
|
27
|
+
|
|
28
|
+
const productOptions = (id: string) =>
|
|
29
|
+
queryOptions({
|
|
30
|
+
queryKey: ["product", id],
|
|
31
|
+
queryFn: () => fetchProduct(id),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function ProductTitle({ id }: { id: string }) {
|
|
35
|
+
const title = useSuspenseQuery({
|
|
36
|
+
...productOptions(id),
|
|
37
|
+
select: (product) => product.title,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return <h1>{title.data}</h1>;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Stabilize expensive selectors
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
const selectAverageRating = (products: Array<Product>) => expensiveAverage(products);
|
|
48
|
+
|
|
49
|
+
function ProductSummary({ filters }: { filters: ProductFilters }) {
|
|
50
|
+
return useSuspenseQuery({
|
|
51
|
+
...productsOptions(filters),
|
|
52
|
+
select: selectAverageRating,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use `React.useCallback` when the selector closes over component props. Move it outside the component when it has no dependencies.
|
|
58
|
+
|
|
59
|
+
### Derive client state instead of syncing it
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
const selectedUser = usersQuery.data?.find((user) => user.id === selectedUserId);
|
|
63
|
+
const visibleSelectedUserId = selectedUser ? selectedUserId : null;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When server data changes, derived values update naturally during render.
|
|
67
|
+
|
|
68
|
+
## Common Mistakes
|
|
69
|
+
|
|
70
|
+
### HIGH Syncing derived state through an effect
|
|
71
|
+
|
|
72
|
+
Wrong:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
if (!users?.some((user) => user.id === selectedUserId)) {
|
|
77
|
+
setSelectedUserId(null);
|
|
78
|
+
}
|
|
79
|
+
}, [users, selectedUserId]);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Correct:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
const selectedUser = users?.find((user) => user.id === selectedUserId);
|
|
86
|
+
const visibleSelectedUserId = selectedUser ? selectedUserId : null;
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Prefer deriving client state from server state during render when no side effect is required.
|
|
90
|
+
|
|
91
|
+
### MEDIUM Inline expensive select reruns on unrelated renders
|
|
92
|
+
|
|
93
|
+
Wrong:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
useSuspenseQuery({
|
|
97
|
+
...productsOptions(filters),
|
|
98
|
+
select: (data) => expensiveSuperTransformation(data),
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Correct:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
const selectProducts = (data: Array<Product>) => expensiveSuperTransformation(data);
|
|
106
|
+
|
|
107
|
+
useSuspenseQuery({
|
|
108
|
+
...productsOptions(filters),
|
|
109
|
+
select: selectProducts,
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
TanStack Query reruns `select` when data changes or the selector function identity changes.
|
|
114
|
+
|
|
115
|
+
### MEDIUM Using select to throw domain errors
|
|
116
|
+
|
|
117
|
+
Wrong:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
useQuery({
|
|
121
|
+
queryKey: ["todos"],
|
|
122
|
+
queryFn: fetchTodos,
|
|
123
|
+
select: (todos) => {
|
|
124
|
+
if (!todos.length) throw new Error("No todos");
|
|
125
|
+
return todos;
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Correct:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
useQuery({
|
|
134
|
+
queryKey: ["todos"],
|
|
135
|
+
queryFn: async () => {
|
|
136
|
+
const todos = await fetchTodos();
|
|
137
|
+
if (!todos.length) throw new Error("No todos");
|
|
138
|
+
return todos;
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`select` transforms successful data. Query errors belong in the query function.
|
|
144
|
+
|
|
145
|
+
Source: TanStack/query:docs/framework/react/guides/render-optimizations.md
|