@hitch42/applet 0.1.0-beta.10
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/LICENSE +21 -0
- package/dist/_internal/client.d.ts +7 -0
- package/dist/_internal/client.js +169 -0
- package/dist/_internal/server.d.ts +92 -0
- package/dist/_internal/server.js +181 -0
- package/dist/applet.d.ts +1 -0
- package/dist/applet.js +184 -0
- package/dist/batch-Qa4B4-Yp.d.ts +30 -0
- package/dist/build-BlmLiKro.js +90 -0
- package/dist/build-CcT-wNN9.js +2 -0
- package/dist/ci-0Q2mUh53.js +68 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.js +38 -0
- package/dist/contract-QUvEGiRI.d.ts +23 -0
- package/dist/deploy-BbL3GObV.js +2579 -0
- package/dist/email-DBTVhNB_.js +62 -0
- package/dist/generate-XB-72Fzj.js +537 -0
- package/dist/generate-wIN3IYqL.js +2 -0
- package/dist/index.d.ts +185 -0
- package/dist/index.js +251 -0
- package/dist/init-DwsXRUk-.js +244 -0
- package/dist/lazy-client-x0DCI98S.js +64 -0
- package/dist/load-config-OKZU3UKn.js +27 -0
- package/dist/login-BBO74hPC.js +12 -0
- package/dist/logs-XvK6Zs5Y.js +183 -0
- package/dist/manifest-4ZAc6HUk.js +191 -0
- package/dist/manifest-I-9s8R36.d.ts +88 -0
- package/dist/me-BW7Paary.js +32 -0
- package/dist/project-B0qNXnFu.js +536 -0
- package/dist/prompt-ChS-grYO.js +29 -0
- package/dist/query-cache-CXmAyLv3.js +82 -0
- package/dist/react.d.ts +37 -0
- package/dist/react.js +515 -0
- package/dist/schedules-BkWZsbSs.js +42 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +104 -0
- package/dist/telephony-protocol-D-nf3Gca.d.ts +76 -0
- package/dist/telephony.d.ts +77 -0
- package/dist/telephony.js +607 -0
- package/dist/upload-DKZzz5kW.d.ts +112 -0
- package/dist/upload-DRbVhuDm.js +114 -0
- package/dist/variables-0tjnAeut.js +108 -0
- package/dist/vite.d.ts +63 -0
- package/dist/vite.js +816 -0
- package/dist/workflow-CTo2kZF1.js +51 -0
- package/dist/workflow-DurM6Fwx.d.ts +28 -0
- package/dist/workflow-engine-MogejNfF.js +117 -0
- package/dist/workspace-DruAZPmV.js +390 -0
- package/package.json +112 -0
- package/scaffold/.agents/skills/hitch-applets/SKILL.md +163 -0
- package/scaffold/.agents/skills/hitch-applets/references/client.md +73 -0
- package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
- package/scaffold/.agents/skills/hitch-applets/references/files.md +109 -0
- package/scaffold/.agents/skills/hitch-applets/references/manifest.md +198 -0
- package/scaffold/.agents/skills/hitch-applets/references/markdown.md +37 -0
- package/scaffold/.agents/skills/hitch-applets/references/performance.md +74 -0
- package/scaffold/.agents/skills/hitch-applets/references/records.md +176 -0
- package/scaffold/.agents/skills/hitch-applets/references/schedules.md +91 -0
- package/scaffold/.agents/skills/hitch-applets/references/server.md +95 -0
- package/scaffold/.agents/skills/hitch-applets/references/workflows.md +81 -0
- 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 +38 -0
- package/scaffold/applet/components.json +25 -0
- package/scaffold/applet/src/client/components/ui/alert.tsx +73 -0
- package/scaffold/applet/src/client/components/ui/avatar.tsx +100 -0
- package/scaffold/applet/src/client/components/ui/badge.tsx +55 -0
- package/scaffold/applet/src/client/components/ui/button.tsx +90 -0
- package/scaffold/applet/src/client/components/ui/card.tsx +85 -0
- package/scaffold/applet/src/client/components/ui/checkbox.tsx +35 -0
- package/scaffold/applet/src/client/components/ui/dialog.tsx +164 -0
- package/scaffold/applet/src/client/components/ui/dropdown-menu.tsx +235 -0
- package/scaffold/applet/src/client/components/ui/input-group.tsx +144 -0
- package/scaffold/applet/src/client/components/ui/input.tsx +22 -0
- package/scaffold/applet/src/client/components/ui/label.tsx +29 -0
- package/scaffold/applet/src/client/components/ui/popover.tsx +74 -0
- package/scaffold/applet/src/client/components/ui/select.tsx +243 -0
- package/scaffold/applet/src/client/components/ui/separator.tsx +25 -0
- package/scaffold/applet/src/client/components/ui/sheet.tsx +177 -0
- package/scaffold/applet/src/client/components/ui/skeleton.tsx +13 -0
- package/scaffold/applet/src/client/components/ui/spinner.tsx +17 -0
- package/scaffold/applet/src/client/components/ui/switch.tsx +41 -0
- package/scaffold/applet/src/client/components/ui/table.tsx +114 -0
- package/scaffold/applet/src/client/components/ui/tabs.tsx +78 -0
- package/scaffold/applet/src/client/components/ui/textarea.tsx +23 -0
- package/scaffold/applet/src/client/lib/utils.ts +1 -0
- package/scaffold/applet/src/client/styles/globals.css +136 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/compose-query-with-tanstack-router-and-start
|
|
3
|
+
description: >
|
|
4
|
+
Use this when combining TanStack Query with TanStack Router or TanStack Start:
|
|
5
|
+
router context QueryClient, createFileRoute loaders, ensureQueryData,
|
|
6
|
+
@tanstack/react-router-ssr-query, setupRouterSsrQueryIntegration, SSR hydration,
|
|
7
|
+
streaming, redirects, and Start server functions.
|
|
8
|
+
type: composition
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- lifecycle/setup-query-client-and-providers
|
|
13
|
+
- core/design-query-keys-and-options
|
|
14
|
+
- lifecycle/prefetch-and-remove-request-waterfalls
|
|
15
|
+
sources:
|
|
16
|
+
- TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
17
|
+
- TanStack/router:https://tanstack.com/router/latest/docs/how-to/setup-ssr
|
|
18
|
+
- TanStack/start:https://tanstack.com/start/latest/docs/framework/react/overview
|
|
19
|
+
- TanStack/query:docs/framework/react/guides/prefetching.md
|
|
20
|
+
- TanStack/query:docs/framework/react/guides/ssr.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { QueryClient, QueryClientProvider, useSuspenseQuery } from "@tanstack/react-query";
|
|
27
|
+
import { createFileRoute, createRouter, RouterProvider } from "@tanstack/react-router";
|
|
28
|
+
import { routeTree } from "./routeTree.gen";
|
|
29
|
+
|
|
30
|
+
const queryClient = new QueryClient();
|
|
31
|
+
|
|
32
|
+
const postsQuery = {
|
|
33
|
+
queryKey: ["posts"],
|
|
34
|
+
queryFn: async () => [{ id: 1, title: "Router first" }],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Route = createFileRoute("/posts")({
|
|
38
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
39
|
+
component: PostsPage,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function PostsPage() {
|
|
43
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
44
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
48
|
+
|
|
49
|
+
export function App() {
|
|
50
|
+
return (
|
|
51
|
+
<QueryClientProvider client={queryClient}>
|
|
52
|
+
<RouterProvider router={router} />
|
|
53
|
+
</QueryClientProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Core Integration Patterns
|
|
59
|
+
|
|
60
|
+
### Put QueryClient in router context
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
64
|
+
import { createRouter } from "@tanstack/react-router";
|
|
65
|
+
import { routeTree } from "./routeTree.gen";
|
|
66
|
+
|
|
67
|
+
const queryClient = new QueryClient();
|
|
68
|
+
|
|
69
|
+
export const router = createRouter({
|
|
70
|
+
routeTree,
|
|
71
|
+
context: { queryClient },
|
|
72
|
+
defaultPreloadStaleTime: 0,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Set `defaultPreloadStaleTime: 0` when Query owns server-state freshness. Router still preloads and runs loaders, but Query is the cache authority.
|
|
77
|
+
|
|
78
|
+
### Use loader ensureQueryData for route data
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
82
|
+
|
|
83
|
+
const todoQuery = (todoId: string) => ({
|
|
84
|
+
queryKey: ["todo", todoId],
|
|
85
|
+
queryFn: async () => ({ id: todoId, title: "Loaded" }),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export const Route = createFileRoute("/todos/$todoId")({
|
|
89
|
+
loader: ({ context, params }) => context.queryClient.ensureQueryData(todoQuery(params.todoId)),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Treat loaders as event handlers that prime the Query cache. Components should still call `useQuery` or `useSuspenseQuery` so the route has an active Query observer.
|
|
94
|
+
|
|
95
|
+
### Prefer Router SSR Query integration for Router SSR
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
99
|
+
import { createRouter } from "@tanstack/react-router";
|
|
100
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
101
|
+
import { routeTree } from "./routeTree.gen";
|
|
102
|
+
|
|
103
|
+
const queryClient = new QueryClient();
|
|
104
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
105
|
+
|
|
106
|
+
setupRouterSsrQueryIntegration({ router, queryClient });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Common Mistakes
|
|
110
|
+
|
|
111
|
+
### CRITICAL Component-only Query creates route waterfall
|
|
112
|
+
|
|
113
|
+
Wrong:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
117
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
118
|
+
|
|
119
|
+
export const Route = createFileRoute("/posts")({ component: PostsPage });
|
|
120
|
+
function PostsPage() {
|
|
121
|
+
const { data } = useSuspenseQuery({
|
|
122
|
+
queryKey: ["posts"],
|
|
123
|
+
queryFn: async () => [{ id: 1 }],
|
|
124
|
+
});
|
|
125
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Correct:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
133
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
134
|
+
|
|
135
|
+
const postsQuery = { queryKey: ["posts"], queryFn: async () => [{ id: 1 }] };
|
|
136
|
+
export const Route = createFileRoute("/posts")({
|
|
137
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
138
|
+
component: PostsPage,
|
|
139
|
+
});
|
|
140
|
+
function PostsPage() {
|
|
141
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
142
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The router can load route-critical data before component render.
|
|
147
|
+
|
|
148
|
+
Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
149
|
+
|
|
150
|
+
### CRITICAL Hand-rolled Router hydration
|
|
151
|
+
|
|
152
|
+
Wrong:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
156
|
+
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
|
157
|
+
import { routeTree } from "./routeTree.gen";
|
|
158
|
+
|
|
159
|
+
const queryClient = new QueryClient();
|
|
160
|
+
const router = createRouter({ routeTree });
|
|
161
|
+
export function App() {
|
|
162
|
+
return (
|
|
163
|
+
<QueryClientProvider client={queryClient}>
|
|
164
|
+
<RouterProvider router={router} />
|
|
165
|
+
</QueryClientProvider>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Correct:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
174
|
+
import { createRouter } from "@tanstack/react-router";
|
|
175
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
176
|
+
import { routeTree } from "./routeTree.gen";
|
|
177
|
+
|
|
178
|
+
const queryClient = new QueryClient();
|
|
179
|
+
const router = createRouter({ routeTree, context: { queryClient } });
|
|
180
|
+
setupRouterSsrQueryIntegration({ router, queryClient });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The Router SSR Query integration handles dehydration, hydration, streamed query results, redirects, and provider wrapping.
|
|
184
|
+
|
|
185
|
+
Source: TanStack/router:https://tanstack.com/router/latest/docs/integrations/query
|
|
186
|
+
|
|
187
|
+
### HIGH Next.js mental model copied into Start
|
|
188
|
+
|
|
189
|
+
Wrong:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
export default async function Page() {
|
|
193
|
+
const posts = await Promise.resolve([{ id: 1 }]);
|
|
194
|
+
return <pre>{JSON.stringify(posts)}</pre>;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Correct:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
202
|
+
|
|
203
|
+
export const Route = createFileRoute("/posts")({
|
|
204
|
+
loader: ({ context }) =>
|
|
205
|
+
context.queryClient.ensureQueryData({
|
|
206
|
+
queryKey: ["posts"],
|
|
207
|
+
queryFn: async () => [{ id: 1 }],
|
|
208
|
+
}),
|
|
209
|
+
component: () => <p>Posts loaded by the route</p>,
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Start is powered by TanStack Router; use file routes, loaders, server functions, and Router SSR before Next-specific app/pages APIs.
|
|
214
|
+
|
|
215
|
+
Source: TanStack/start:https://tanstack.com/start/latest/docs/framework/react/overview
|
|
216
|
+
|
|
217
|
+
### HIGH Reading loader data instead of observing Query
|
|
218
|
+
|
|
219
|
+
Wrong:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import { createFileRoute, useLoaderData } from "@tanstack/react-router";
|
|
223
|
+
|
|
224
|
+
export const Route = createFileRoute("/posts")({
|
|
225
|
+
loader: ({ context }) => context.queryClient.ensureQueryData(postsQuery),
|
|
226
|
+
component: PostsPage,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
function PostsPage() {
|
|
230
|
+
const posts = useLoaderData({ from: "/posts" });
|
|
231
|
+
return <pre>{JSON.stringify(posts)}</pre>;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Correct:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
239
|
+
|
|
240
|
+
function PostsPage() {
|
|
241
|
+
const { data } = useSuspenseQuery(postsQuery);
|
|
242
|
+
return <pre>{JSON.stringify(data)}</pre>;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The loader primes the cache. The component still needs an active Query observer for refetch triggers, invalidation, and garbage collection semantics.
|
|
247
|
+
|
|
248
|
+
See also: `lifecycle/ssr-hydration-and-streaming` for framework SSR recipes.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/enforce-query-best-practices-with-eslint
|
|
3
|
+
description: >
|
|
4
|
+
Use this when configuring @tanstack/eslint-plugin-query, flat/recommended,
|
|
5
|
+
flat/recommended-strict, exhaustive-deps, no-rest-destructuring,
|
|
6
|
+
no-unstable-deps, no-void-query-fn, stable-query-client, prefer-query-options,
|
|
7
|
+
infinite-query-property-order, and mutation-property-order.
|
|
8
|
+
type: composition
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- core/design-query-keys-and-options
|
|
13
|
+
- core/fetch-and-observe-queries
|
|
14
|
+
- framework/shape-data-and-render-efficiently
|
|
15
|
+
sources:
|
|
16
|
+
- TanStack/query:docs/eslint/eslint-plugin-query.md
|
|
17
|
+
- TanStack/query:docs/eslint/exhaustive-deps.md
|
|
18
|
+
- TanStack/query:docs/eslint/no-rest-destructuring.md
|
|
19
|
+
- TanStack/query:docs/eslint/no-unstable-deps.md
|
|
20
|
+
- TanStack/query:docs/eslint/no-void-query-fn.md
|
|
21
|
+
- TanStack/query:docs/eslint/prefer-query-options.md
|
|
22
|
+
- TanStack/query:docs/eslint/stable-query-client.md
|
|
23
|
+
- TanStack/query:docs/eslint/infinite-query-property-order.md
|
|
24
|
+
- TanStack/query:docs/eslint/mutation-property-order.md
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
31
|
+
|
|
32
|
+
export default [...pluginQuery.configs["flat/recommended"]];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Core Integration Patterns
|
|
36
|
+
|
|
37
|
+
### Use strict when generating Query-heavy code
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
41
|
+
|
|
42
|
+
export default [...pluginQuery.configs["flat/recommended-strict"]];
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Prefer option factories
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { queryOptions, useQuery } from "@tanstack/react-query";
|
|
49
|
+
|
|
50
|
+
const todosOptions = queryOptions({
|
|
51
|
+
queryKey: ["todos"],
|
|
52
|
+
queryFn: async () => [{ id: 1 }],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export function useTodos() {
|
|
56
|
+
return useQuery(todosOptions);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Keep inference-sensitive property order
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
64
|
+
|
|
65
|
+
export function useFeed() {
|
|
66
|
+
return useInfiniteQuery({
|
|
67
|
+
queryKey: ["feed"],
|
|
68
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
69
|
+
initialPageParam: 0,
|
|
70
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Common Mistakes
|
|
76
|
+
|
|
77
|
+
### MEDIUM Strict rule not enabled
|
|
78
|
+
|
|
79
|
+
Wrong:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
83
|
+
|
|
84
|
+
export default [...pluginQuery.configs["flat/recommended"]];
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Correct:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import pluginQuery from "@tanstack/eslint-plugin-query";
|
|
91
|
+
|
|
92
|
+
export default [...pluginQuery.configs["flat/recommended-strict"]];
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Strict mode catches option-factory and inference patterns that agents commonly miss.
|
|
96
|
+
|
|
97
|
+
Source: TanStack/query:docs/eslint/eslint-plugin-query.md
|
|
98
|
+
|
|
99
|
+
### MEDIUM Infinite option property order
|
|
100
|
+
|
|
101
|
+
Wrong:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
105
|
+
|
|
106
|
+
export function useFeed() {
|
|
107
|
+
return useInfiniteQuery({
|
|
108
|
+
queryFn: async () => ({ nextCursor: 1 }),
|
|
109
|
+
queryKey: ["feed"],
|
|
110
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
111
|
+
initialPageParam: 0,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Correct:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
120
|
+
|
|
121
|
+
export function useFeed() {
|
|
122
|
+
return useInfiniteQuery({
|
|
123
|
+
queryKey: ["feed"],
|
|
124
|
+
queryFn: async ({ pageParam }) => ({ nextCursor: pageParam + 1 }),
|
|
125
|
+
initialPageParam: 0,
|
|
126
|
+
getNextPageParam: (page) => page.nextCursor,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Some infinite-query inference depends on stable option ordering.
|
|
132
|
+
|
|
133
|
+
Source: TanStack/query:docs/eslint/infinite-query-property-order.md
|
|
134
|
+
|
|
135
|
+
### MEDIUM Mutation option property order
|
|
136
|
+
|
|
137
|
+
Wrong:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { useMutation } from "@tanstack/react-query";
|
|
141
|
+
|
|
142
|
+
export function useSave() {
|
|
143
|
+
return useMutation({
|
|
144
|
+
onSuccess: () => console.log("saved"),
|
|
145
|
+
mutationFn: async (title: string) => title,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Correct:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { useMutation } from "@tanstack/react-query";
|
|
154
|
+
|
|
155
|
+
export function useSave() {
|
|
156
|
+
return useMutation({
|
|
157
|
+
mutationFn: async (title: string) => title,
|
|
158
|
+
onSuccess: () => console.log("saved"),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The mutation property order rule preserves inference for mutation options.
|
|
164
|
+
|
|
165
|
+
Source: TanStack/query:docs/eslint/mutation-property-order.md
|
|
166
|
+
|
|
167
|
+
See also: `core/design-query-keys-and-options` for the patterns these rules protect.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions/persist-offline-and-restore-caches
|
|
3
|
+
description: >
|
|
4
|
+
Use this when using persistQueryClient, PersistQueryClientProvider,
|
|
5
|
+
createSyncStoragePersister, createAsyncStoragePersister, experimental
|
|
6
|
+
fine-grained persisters, offline mutations, resumePausedMutations, maxAge,
|
|
7
|
+
gcTime, and restore races.
|
|
8
|
+
type: composition
|
|
9
|
+
library: TanStack Query
|
|
10
|
+
library_version: "5.101.0"
|
|
11
|
+
requires:
|
|
12
|
+
- lifecycle/setup-query-client-and-providers
|
|
13
|
+
- core/tune-defaults-freshness-retries-and-refetching
|
|
14
|
+
- core/write-mutations-and-invalidate-related-queries
|
|
15
|
+
sources:
|
|
16
|
+
- TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
17
|
+
- TanStack/query:docs/framework/react/plugins/createSyncStoragePersister.md
|
|
18
|
+
- TanStack/query:docs/framework/react/plugins/createAsyncStoragePersister.md
|
|
19
|
+
- TanStack/query:docs/framework/react/plugins/createPersister.md
|
|
20
|
+
- TanStack/query:docs/framework/react/guides/network-mode.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import * as React from "react";
|
|
27
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
28
|
+
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
|
|
29
|
+
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
|
30
|
+
|
|
31
|
+
const queryClient = new QueryClient({
|
|
32
|
+
defaultOptions: { queries: { gcTime: 24 * 60 * 60 * 1000 } },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const persister = createSyncStoragePersister({
|
|
36
|
+
storage: window.localStorage,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export function AppProviders(props: { children: React.ReactNode }) {
|
|
40
|
+
return (
|
|
41
|
+
<PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
|
|
42
|
+
{props.children}
|
|
43
|
+
</PersistQueryClientProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Core Integration Patterns
|
|
49
|
+
|
|
50
|
+
### Resume paused mutations after restore
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
54
|
+
|
|
55
|
+
export const queryClient = new QueryClient();
|
|
56
|
+
|
|
57
|
+
queryClient.setMutationDefaults(["todos"], {
|
|
58
|
+
mutationFn: async (todo: { id: number; title: string }) => todo,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export function resumeMutations() {
|
|
62
|
+
return queryClient.resumePausedMutations();
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Align cache lifetime with persistence
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
70
|
+
|
|
71
|
+
export const queryClient = new QueryClient({
|
|
72
|
+
defaultOptions: {
|
|
73
|
+
queries: { gcTime: 7 * 24 * 60 * 60 * 1000 },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Use networkMode for offline-first writes
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { useMutation } from "@tanstack/react-query";
|
|
82
|
+
|
|
83
|
+
export function useSaveDraft() {
|
|
84
|
+
return useMutation({
|
|
85
|
+
mutationKey: ["saveDraft"],
|
|
86
|
+
mutationFn: async (draft: { body: string }) => draft,
|
|
87
|
+
networkMode: "offlineFirst",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Common Mistakes
|
|
93
|
+
|
|
94
|
+
### HIGH gcTime shorter than maxAge
|
|
95
|
+
|
|
96
|
+
Wrong:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
100
|
+
|
|
101
|
+
export const queryClient = new QueryClient({
|
|
102
|
+
defaultOptions: { queries: { gcTime: 5 * 60 * 1000 } },
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Correct:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
110
|
+
|
|
111
|
+
export const queryClient = new QueryClient({
|
|
112
|
+
defaultOptions: { queries: { gcTime: 24 * 60 * 60 * 1000 } },
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Persisted data can be garbage-collected before the persister maxAge can restore it.
|
|
117
|
+
|
|
118
|
+
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
119
|
+
|
|
120
|
+
### CRITICAL Rendering before restore
|
|
121
|
+
|
|
122
|
+
Wrong:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
|
|
126
|
+
|
|
127
|
+
const queryClient = new QueryClient();
|
|
128
|
+
export function App(props: { children: React.ReactNode }) {
|
|
129
|
+
return <QueryClientProvider client={queryClient}>{props.children}</QueryClientProvider>;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Correct:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
137
|
+
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
|
|
138
|
+
import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
|
|
139
|
+
|
|
140
|
+
const queryClient = new QueryClient();
|
|
141
|
+
const persister = createSyncStoragePersister({ storage: window.localStorage });
|
|
142
|
+
|
|
143
|
+
export function App(props: { children: React.ReactNode }) {
|
|
144
|
+
return (
|
|
145
|
+
<PersistQueryClientProvider client={queryClient} persistOptions={{ persister }}>
|
|
146
|
+
{props.children}
|
|
147
|
+
</PersistQueryClientProvider>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The persistence provider prevents query fetching while restore is in progress.
|
|
153
|
+
|
|
154
|
+
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
155
|
+
|
|
156
|
+
### CRITICAL Missing default mutationFn
|
|
157
|
+
|
|
158
|
+
Wrong:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
162
|
+
|
|
163
|
+
export const queryClient = new QueryClient();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Correct:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
170
|
+
|
|
171
|
+
export const queryClient = new QueryClient();
|
|
172
|
+
queryClient.setMutationDefaults(["saveDraft"], {
|
|
173
|
+
mutationFn: async (draft: { body: string }) => draft,
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Paused persisted mutations cannot resume without a serializable mutation key mapped to a default mutationFn.
|
|
178
|
+
|
|
179
|
+
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
|
|
180
|
+
|
|
181
|
+
See also: `core/tune-defaults-freshness-retries-and-refetching` for gcTime and networkMode.
|