@raf-query/react 0.0.11 → 0.0.13
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/CHANGELOG.md +11 -1
- package/README.md +23 -10
- package/dist/index.d.mts +2 -2
- package/dist/use-query.d.mts +14 -1
- package/dist/use-query.mjs +2 -13
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.13](https://github.com/KATT/raf-query/compare/0.0.12...0.0.13) (2026-10-12)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* useQuery returns data for handles from ensure ([#16](https://github.com/KATT/raf-query/issues/16)) ([a50b8fd](https://github.com/KATT/raf-query/commit/a50b8fdc51b952341377d4b55f25ebb6b21d39e1))
|
|
8
|
+
|
|
9
|
+
## [0.0.12](https://github.com/KATT/raf-query/compare/0.0.11...0.0.12) (2026-10-12)
|
|
10
|
+
|
|
11
|
+
- **examples:** move the shared db and ui packages into examples/.shared ([#14](https://github.com/KATT/raf-query/issues/14)) ([fccdc7b](https://github.com/KATT/raf-query/commit/fccdc7bb8075ce2fb0cdbe07b26b36f2ace2bd9c))
|
|
12
|
+
|
|
3
13
|
## [0.0.11](https://github.com/KATT/raf-query/compare/0.0.10...0.0.11) (2026-10-12)
|
|
4
14
|
|
|
5
15
|
### Features
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
- **core:** structurally share new data with the old (structuralSharing) ([#15](https://github.com/KATT/raf-query/issues/15)) ([bf4392a](https://github.com/KATT/raf-query/commit/bf4392a56936ead6fb8b2726981cb19dadf51304))
|
|
8
18
|
|
|
9
19
|
## [0.0.10](https://github.com/KATT/raf-query/compare/0.0.9...0.0.10) (2026-10-12)
|
|
10
20
|
|
package/README.md
CHANGED
|
@@ -72,11 +72,11 @@ The factory is the only place a query is declared paged. Every other API — `pr
|
|
|
72
72
|
|
|
73
73
|
All three verbs live on the query client and accept a factory plus its input.
|
|
74
74
|
|
|
75
|
-
| Call | Returns
|
|
76
|
-
| --------------------------------- |
|
|
77
|
-
| `qc.prefetch(factory, input)` | `QueryHandle`
|
|
78
|
-
| `await qc.ensure(factory, input)` | `Promise<
|
|
79
|
-
| `await qc.fetch(factory, input)` | `Promise<Data>`
|
|
75
|
+
| Call | Returns | Blocks? | Use for |
|
|
76
|
+
| --------------------------------- | ----------------------------- | ------- | ---------------------------------------------------------------------- |
|
|
77
|
+
| `qc.prefetch(factory, input)` | `QueryHandle` | No | Data the route can render without; it streams in |
|
|
78
|
+
| `await qc.ensure(factory, input)` | `Promise<EnsuredQueryHandle>` | Yes | Data the route must not render without; `useQuery` returns it directly |
|
|
79
|
+
| `await qc.fetch(factory, input)` | `Promise<Data>` | Yes | Rendering in a server component, or building another input |
|
|
80
80
|
|
|
81
81
|
```tsx
|
|
82
82
|
// TanStack Start
|
|
@@ -125,9 +125,16 @@ interface QueryHandle<TFactory> {
|
|
|
125
125
|
readonly key: QueryKey; // ["post", "hello"]
|
|
126
126
|
readonly input: InputOf<TFactory>; // { slug: "hello" }
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
// From `ensure`: the route waited for the data.
|
|
130
|
+
interface EnsuredQueryHandle<TFactory> {
|
|
131
|
+
readonly key: QueryKey;
|
|
132
|
+
readonly input: InputOf<TFactory>;
|
|
133
|
+
readonly ensured: true;
|
|
134
|
+
}
|
|
128
135
|
```
|
|
129
136
|
|
|
130
|
-
Handles hold no data and no functions. Data reaches the client cache through the [hydration stream](#ssr-and-hydration), so loader data stays tiny and each query is serialized once. The loader's return value lists exactly which queries a route started, and
|
|
137
|
+
Handles hold no data and no functions. Data reaches the client cache through the [hydration stream](#ssr-and-hydration), so loader data stays tiny and each query is serialized once. The loader's return value lists exactly which queries a route started, and its types show which ones the route waited for: `useQuery` returns the data for an `EnsuredQueryHandle` and a promise for a `QueryHandle`. The two aren't assignable to each other, so type a prop with the one its loader returns.
|
|
131
138
|
|
|
132
139
|
## Reading queries
|
|
133
140
|
|
|
@@ -135,17 +142,20 @@ Handles hold no data and no functions. Data reaches the client cache through the
|
|
|
135
142
|
|
|
136
143
|
```ts
|
|
137
144
|
const [promise, controls] = useQuery(factory, handle, options?);
|
|
145
|
+
const [data, controls] = useQuery(factory, ensuredHandle, options?);
|
|
138
146
|
```
|
|
139
147
|
|
|
140
148
|
`options` takes [`whilePending`](#when-the-url-changes) and per-read refetch settings (`refetchInterval`, `refetchOnWindowFocus`, `refetchOnReconnect`). There is no option to change the input; the handle decides what is read.
|
|
141
149
|
|
|
142
150
|
`promise` is a stable `Promise<Data>` owned by the cache. Unwrap it with `use()`. When the cache already has data, the promise is pre-resolved and `use()` returns synchronously without suspending.
|
|
143
151
|
|
|
152
|
+
A handle from `ensure` had its data loaded before the route rendered, so `useQuery` returns the data itself. If the entry has lost that data since (it was garbage-collected while the router kept the loader data, or `reset`), the hook suspends until it is refetched.
|
|
153
|
+
|
|
144
154
|
```tsx
|
|
145
155
|
function PostPage() {
|
|
146
156
|
const loaderData = Route.useLoaderData();
|
|
147
|
-
|
|
148
|
-
const post =
|
|
157
|
+
// `post` came from `qc.ensure`, `comments` from `qc.prefetch`.
|
|
158
|
+
const [post, postState] = useQuery(postQuery, loaderData.post);
|
|
149
159
|
|
|
150
160
|
return (
|
|
151
161
|
<article>
|
|
@@ -267,6 +277,7 @@ Either way, an input that's already cached renders immediately. A component that
|
|
|
267
277
|
To show a skeleton instead, for instance when moving between unrelated posts, give the Suspense boundary the query's key. React treats it as new content and shows its fallback, even inside a transition:
|
|
268
278
|
|
|
269
279
|
```tsx
|
|
280
|
+
// This loader prefetches the post instead of ensuring it.
|
|
270
281
|
const [postPromise, postState] = useQuery(postQuery, loaderData.post);
|
|
271
282
|
|
|
272
283
|
return (
|
|
@@ -282,10 +293,10 @@ In Next.js the same rules apply: read `searchParams` in the page, `prefetch` wit
|
|
|
282
293
|
|
|
283
294
|
### Passing promises down
|
|
284
295
|
|
|
285
|
-
A component can call the hook and hand the promise to a child, so only the child suspends:
|
|
296
|
+
A component can call the hook with a prefetched handle and hand the promise to a child, so only the child suspends:
|
|
286
297
|
|
|
287
298
|
```tsx
|
|
288
|
-
const [postPromise] = useQuery(postQuery, loaderData.post);
|
|
299
|
+
const [postPromise] = useQuery(postQuery, loaderData.post); // from `qc.prefetch`
|
|
289
300
|
return (
|
|
290
301
|
<Suspense fallback={<Spinner />}>
|
|
291
302
|
<PostBody post={postPromise} />
|
|
@@ -435,6 +446,7 @@ export default async function Page(props: {
|
|
|
435
446
|
- `HydrationBoundary` sends the request client's entries to the browser cache; pending ones stream as promises. Render one in each server component that starts queries for client components. Entries already sent in this request are skipped.
|
|
436
447
|
- `QueryProvider` creates the browser client, and streams queries that client components start during SSR (`useWaterfall`) into the HTML. Pass `config` for the client's options; it must be serializable.
|
|
437
448
|
- Server components that only render data can `await qc.fetch(...)` anywhere; the per-request client dedupes them.
|
|
449
|
+
- `fetch` returns data, not a handle. When a server component renders the data and also hands the query to a client component, pass `await qc.ensure(...)`: after the `fetch` it resolves from the cache, and the client component gets the data from `useQuery` without a promise.
|
|
438
450
|
- Factories can call server actions: server components call them directly, and the browser calls them on refetch. Validate their arguments, since anyone can call them.
|
|
439
451
|
|
|
440
452
|
#### Server-only queries
|
|
@@ -555,6 +567,7 @@ Query options, accepted by `queryOptions`, `infiniteQueryOptions` and `config.de
|
|
|
555
567
|
| `hashKey(key)`, `matchesKey(key, prefix)` | Key helpers |
|
|
556
568
|
| `CancelledError` | Rejection of a request cancelled by `clear()` or a restart |
|
|
557
569
|
| `QueryHandle<TFactory>` | Handle type for props |
|
|
570
|
+
| `EnsuredQueryHandle<TFactory>` | Handle type for props fed by `ensure` |
|
|
558
571
|
| `DataOf<TFactory>`, `InputOf<TFactory>` | A factory's data and input types |
|
|
559
572
|
|
|
560
573
|
`@raf-query/react` (plus all of the above):
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { QueryClientProvider, useQueryClient } from "./provider.mjs";
|
|
2
|
-
import { PagingControls, QueryControls, UseQueryControls, UseQueryOptions, UseQueryResult, useQuery } from "./use-query.mjs";
|
|
2
|
+
import { PagingControls, QueryControls, UseEnsuredQueryResult, UseQueryControls, UseQueryOptions, UseQueryResult, useQuery } from "./use-query.mjs";
|
|
3
3
|
import { useWaterfall } from "./use-waterfall.mjs";
|
|
4
4
|
export * from "@raf-query/core";
|
|
5
|
-
export { type PagingControls, QueryClientProvider, type QueryControls, type UseQueryControls, type UseQueryOptions, type UseQueryResult, useQuery, useQueryClient, useWaterfall };
|
|
5
|
+
export { type PagingControls, QueryClientProvider, type QueryControls, type UseEnsuredQueryResult, type UseQueryControls, type UseQueryOptions, type UseQueryResult, useQuery, useQueryClient, useWaterfall };
|
package/dist/use-query.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DataOf, InputOf, IsPaged, QueryClient, QueryFactory, QueryHandle, QueryKey } from "@raf-query/core";
|
|
1
|
+
import { DataOf, EnsuredQueryHandle, InputOf, IsPaged, QueryClient, QueryFactory, QueryHandle, QueryKey } from "@raf-query/core";
|
|
2
2
|
//#region src/use-query.d.ts
|
|
3
3
|
export interface UseQueryOptions {
|
|
4
4
|
/** Refetch on this interval (ms) while mounted and the page is visible. */
|
|
@@ -48,6 +48,8 @@ export interface PagingControls {
|
|
|
48
48
|
}
|
|
49
49
|
export type UseQueryControls<TFactory extends QueryFactory> = IsPaged<TFactory> extends true ? QueryControls<TFactory> & PagingControls : QueryControls<TFactory>;
|
|
50
50
|
export type UseQueryResult<TFactory extends QueryFactory> = readonly [promise: Promise<DataOf<TFactory>>, controls: UseQueryControls<TFactory>];
|
|
51
|
+
/** {@link useQuery}'s result for a handle from `ensure`. */
|
|
52
|
+
export type UseEnsuredQueryResult<TFactory extends QueryFactory> = readonly [data: DataOf<TFactory>, controls: UseQueryControls<TFactory>];
|
|
51
53
|
/**
|
|
52
54
|
* Read the query a handle points at. Returns a promise to unwrap with
|
|
53
55
|
* `use()` and the query's controls:
|
|
@@ -59,6 +61,17 @@ export type UseQueryResult<TFactory extends QueryFactory> = readonly [promise: P
|
|
|
59
61
|
*
|
|
60
62
|
* The promise is stable per data version and already fulfilled once the
|
|
61
63
|
* cache has data, so `use()` only suspends while there is nothing to show.
|
|
64
|
+
*
|
|
65
|
+
* A handle from `ensure` had its data loaded before the route rendered, so
|
|
66
|
+
* the hook returns the data itself:
|
|
67
|
+
*
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const [post, postState] = useQuery(postQuery, loaderData.post);
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* If that entry has lost its data since (garbage-collected, `reset`), the
|
|
73
|
+
* hook suspends until it is refetched.
|
|
62
74
|
*/
|
|
75
|
+
export declare function useQuery<TFactory extends QueryFactory>(factory: TFactory, handle: EnsuredQueryHandle<TFactory>, options?: UseQueryOptions): UseEnsuredQueryResult<TFactory>;
|
|
63
76
|
export declare function useQuery<TFactory extends QueryFactory>(factory: TFactory, handle: QueryHandle<TFactory>, options?: UseQueryOptions): UseQueryResult<TFactory>;
|
|
64
77
|
//#endregion
|
package/dist/use-query.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useQueryClient } from "./provider.mjs";
|
|
3
3
|
import { unsafeCast } from "./cast.mjs";
|
|
4
|
-
import { useCallback, useEffect, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { use, useCallback, useEffect, useState, useSyncExternalStore } from "react";
|
|
5
5
|
//#region src/use-query.ts
|
|
6
6
|
function queryFor(client, factory, input) {
|
|
7
7
|
const args = [input];
|
|
@@ -16,18 +16,6 @@ function useRefetchInterval(query, interval) {
|
|
|
16
16
|
return () => clearInterval(timer);
|
|
17
17
|
}, [query, interval]);
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Read the query a handle points at. Returns a promise to unwrap with
|
|
21
|
-
* `use()` and the query's controls:
|
|
22
|
-
*
|
|
23
|
-
* ```tsx
|
|
24
|
-
* const [postPromise, postState] = useQuery(postQuery, loaderData.post);
|
|
25
|
-
* const post = use(postPromise);
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* The promise is stable per data version and already fulfilled once the
|
|
29
|
-
* cache has data, so `use()` only suspends while there is nothing to show.
|
|
30
|
-
*/
|
|
31
19
|
function useQuery(factory, handle, options = {}) {
|
|
32
20
|
const query = queryFor(useQueryClient(), factory, handle.input);
|
|
33
21
|
query.read();
|
|
@@ -76,6 +64,7 @@ function useQuery(factory, handle, options = {}) {
|
|
|
76
64
|
refetch: query.refetch,
|
|
77
65
|
updatedAt: shown.updatedAt
|
|
78
66
|
};
|
|
67
|
+
if (handle.ensured === true) return unsafeCast([use(shown.promise), controls]);
|
|
79
68
|
return unsafeCast([shown.promise, controls]);
|
|
80
69
|
}
|
|
81
70
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raf-query/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "React bindings for raf-query, a render-as-you-fetch query library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"query",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"./tanstack-router": "./dist/tanstack-router.mjs"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@raf-query/core": "0.0.
|
|
43
|
+
"@raf-query/core": "0.0.13",
|
|
44
44
|
"effect": "^4.0.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|