@raf-query/react 0.0.13 → 0.0.15
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 +15 -14
- package/dist/use-query.d.mts +5 -15
- package/dist/use-query.mjs +23 -52
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.15](https://github.com/KATT/raf-query/compare/0.0.14...0.0.15) (2026-10-12)
|
|
4
|
+
|
|
5
|
+
* **agent-docs:** replace local ~/dev path with upstream React link ([#17](https://github.com/KATT/raf-query/issues/17)) ([79c0314](https://github.com/KATT/raf-query/commit/79c031475c3ed62ce5a07fb91d68733534e452b3))
|
|
6
|
+
|
|
7
|
+
## [0.0.14](https://github.com/KATT/raf-query/compare/0.0.13...0.0.14) (2026-10-12)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- tracked query results via a core QueryObserver, like TanStack Query ([#13](https://github.com/KATT/raf-query/issues/13)) ([7b99b47](https://github.com/KATT/raf-query/commit/7b99b4704be110c999b41277afd5dac4c515f45f))
|
|
12
|
+
|
|
3
13
|
## [0.0.13](https://github.com/KATT/raf-query/compare/0.0.12...0.0.13) (2026-10-12)
|
|
4
14
|
|
|
5
15
|
### Features
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
- 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
18
|
|
|
9
19
|
## [0.0.12](https://github.com/KATT/raf-query/compare/0.0.11...0.0.12) (2026-10-12)
|
|
10
20
|
|
package/README.md
CHANGED
|
@@ -189,7 +189,7 @@ function PostPage() {
|
|
|
189
189
|
| `hash` | Stable string form of the handle's key, for `key` props on Suspense boundaries |
|
|
190
190
|
| `refetch()` | Refetch now. Returns a promise, so it can be awaited inside a transition or action |
|
|
191
191
|
|
|
192
|
-
When the cache updates — refetch, invalidation, `setData` — the hook returns a new, pre-resolved promise and the component re-renders without suspending. New data is structurally shared with the old: objects and arrays that are deeply equal keep their identity, and a refetch that returns equal data keeps the same promise, so nothing re-renders. When the **first** load fails, the promise rejects and `use()` throws to the nearest error boundary. Resetting that boundary refetches the query.
|
|
192
|
+
When the cache updates — refetch, invalidation, `setData` — the hook returns a new, pre-resolved promise and the component re-renders without suspending. New data is structurally shared with the old: objects and arrays that are deeply equal keep their identity, and a refetch that returns equal data keeps the same promise, so nothing re-renders. Controls are tracked: reading `controls.isFetching` subscribes the component to `isFetching`, and a change to a control it never read doesn't re-render it. When the **first** load fails, the promise rejects and `use()` throws to the nearest error boundary. Resetting that boundary refetches the query.
|
|
193
193
|
|
|
194
194
|
Queries do not refetch on mount. They refetch when a loader starts them again, when invalidated, on window focus or reconnect while stale, on `refetch()`, or on `refetchInterval`.
|
|
195
195
|
|
|
@@ -304,7 +304,7 @@ return (
|
|
|
304
304
|
);
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
|
|
307
|
+
A component re-renders when the promise changes, and when a control it reads changes. Controls it never reads, like `isFetching` here, don't re-render it, so a parent that only passes the promise down stays put during background refetches.
|
|
308
308
|
|
|
309
309
|
## Render-time inputs: `useWaterfall`
|
|
310
310
|
|
|
@@ -557,18 +557,19 @@ Query options, accepted by `queryOptions`, `infiniteQueryOptions` and `config.de
|
|
|
557
557
|
|
|
558
558
|
`@raf-query/core`:
|
|
559
559
|
|
|
560
|
-
| Export | Use
|
|
561
|
-
| ---------------------------------------------- |
|
|
562
|
-
| `QueryClient` | The cache
|
|
563
|
-
| `queryOptions(options)` | Typed options for a query factory
|
|
564
|
-
| `infiniteQueryOptions(options)` | Typed options for a paged query factory
|
|
565
|
-
| `allKeyed(record)` | Await a record of promises, keeping keys
|
|
566
|
-
| `dehydrate(client)` / `hydrate(client, state)` | Low-level cache transfer used by the integrations
|
|
567
|
-
| `hashKey(key)`, `matchesKey(key, prefix)` | Key helpers
|
|
568
|
-
| `
|
|
569
|
-
| `
|
|
570
|
-
| `
|
|
571
|
-
| `
|
|
560
|
+
| Export | Use |
|
|
561
|
+
| ---------------------------------------------- | ---------------------------------------------------------------- |
|
|
562
|
+
| `QueryClient` | The cache |
|
|
563
|
+
| `queryOptions(options)` | Typed options for a query factory |
|
|
564
|
+
| `infiniteQueryOptions(options)` | Typed options for a paged query factory |
|
|
565
|
+
| `allKeyed(record)` | Await a record of promises, keeping keys |
|
|
566
|
+
| `dehydrate(client)` / `hydrate(client, state)` | Low-level cache transfer used by the integrations |
|
|
567
|
+
| `hashKey(key)`, `matchesKey(key, prefix)` | Key helpers |
|
|
568
|
+
| `QueryObserver` | One reader's view of a query, with tracked results; for bindings |
|
|
569
|
+
| `CancelledError` | Rejection of a request cancelled by `clear()` or a restart |
|
|
570
|
+
| `QueryHandle<TFactory>` | Handle type for props |
|
|
571
|
+
| `EnsuredQueryHandle<TFactory>` | Handle type for props fed by `ensure` |
|
|
572
|
+
| `DataOf<TFactory>`, `InputOf<TFactory>` | A factory's data and input types |
|
|
572
573
|
|
|
573
574
|
`@raf-query/react` (plus all of the above):
|
|
574
575
|
|
package/dist/use-query.d.mts
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
import { DataOf, EnsuredQueryHandle, InputOf, IsPaged, QueryClient, QueryFactory, QueryHandle, QueryKey } from "@raf-query/core";
|
|
1
|
+
import { DataOf, EnsuredQueryHandle, InputOf, IsPaged, QueryClient, QueryFactory, QueryHandle, QueryKey, QueryObserverOptions } from "@raf-query/core";
|
|
2
2
|
//#region src/use-query.d.ts
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
readonly refetchInterval?: number;
|
|
6
|
-
/** Refetch on reconnect while stale. Default: the query's option, else `true`. */
|
|
7
|
-
readonly refetchOnReconnect?: boolean;
|
|
8
|
-
/** Refetch on window focus while stale. Default: the query's option, else `true`. */
|
|
9
|
-
readonly refetchOnWindowFocus?: boolean;
|
|
10
|
-
/**
|
|
11
|
-
* What to render while the handle's input has no data yet:
|
|
12
|
-
* - `"suspend"` (default): the promise is pending, so `use()` suspends.
|
|
13
|
-
* - `"previous"`: keep returning the last input's data, with `isPrevious`.
|
|
14
|
-
*/
|
|
15
|
-
readonly whilePending?: "previous" | "suspend";
|
|
16
|
-
}
|
|
3
|
+
/** `refetchInterval`, `refetchOnWindowFocus`, `refetchOnReconnect`, `whilePending`. */
|
|
4
|
+
export type UseQueryOptions = QueryObserverOptions;
|
|
17
5
|
/** The second element of {@link useQuery}'s result. */
|
|
18
6
|
export interface QueryControls<TFactory extends QueryFactory> {
|
|
19
7
|
/** Error of the last failed request. Existing data stays readable. */
|
|
@@ -61,6 +49,8 @@ export type UseEnsuredQueryResult<TFactory extends QueryFactory> = readonly [dat
|
|
|
61
49
|
*
|
|
62
50
|
* The promise is stable per data version and already fulfilled once the
|
|
63
51
|
* cache has data, so `use()` only suspends while there is nothing to show.
|
|
52
|
+
* The component re-renders when the promise changes, and when a control it
|
|
53
|
+
* has read changes; changes to controls it never reads are skipped.
|
|
64
54
|
*
|
|
65
55
|
* A handle from `ensure` had its data loaded before the route rendered, so
|
|
66
56
|
* the hook returns the data itself:
|
package/dist/use-query.mjs
CHANGED
|
@@ -1,71 +1,42 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useQueryClient } from "./provider.mjs";
|
|
3
3
|
import { unsafeCast } from "./cast.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { QueryObserver } from "@raf-query/core";
|
|
5
|
+
import { use, useEffect, useState, useSyncExternalStore } from "react";
|
|
5
6
|
//#region src/use-query.ts
|
|
6
7
|
function queryFor(client, factory, input) {
|
|
7
8
|
const args = [input];
|
|
8
9
|
return client.getQuery(factory, ...args);
|
|
9
10
|
}
|
|
10
|
-
function useRefetchInterval(query, interval) {
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (interval === void 0 || !Number.isFinite(interval) || interval <= 0) return;
|
|
13
|
-
const timer = setInterval(() => {
|
|
14
|
-
if (document.visibilityState !== "hidden") query.refetch();
|
|
15
|
-
}, interval);
|
|
16
|
-
return () => clearInterval(timer);
|
|
17
|
-
}, [query, interval]);
|
|
18
|
-
}
|
|
19
11
|
function useQuery(factory, handle, options = {}) {
|
|
20
12
|
const query = queryFor(useQueryClient(), factory, handle.input);
|
|
21
13
|
query.read();
|
|
14
|
+
const refetchInterval = options.refetchInterval;
|
|
22
15
|
const refetchOnReconnect = options.refetchOnReconnect;
|
|
23
16
|
const refetchOnWindowFocus = options.refetchOnWindowFocus;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
const whilePending = options.whilePending;
|
|
18
|
+
const [observer] = useState(() => new QueryObserver(query, options));
|
|
19
|
+
const result = observer.getOptimisticResult(query, options);
|
|
20
|
+
useSyncExternalStore(observer.subscribe, observer.getCurrentResult, observer.getCurrentResult);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
observer.setOptions(query, {
|
|
23
|
+
refetchInterval,
|
|
24
|
+
refetchOnReconnect,
|
|
25
|
+
refetchOnWindowFocus,
|
|
26
|
+
whilePending
|
|
27
|
+
});
|
|
28
|
+
}, [
|
|
29
|
+
observer,
|
|
28
30
|
query,
|
|
31
|
+
refetchInterval,
|
|
29
32
|
refetchOnReconnect,
|
|
30
|
-
refetchOnWindowFocus
|
|
33
|
+
refetchOnWindowFocus,
|
|
34
|
+
whilePending
|
|
31
35
|
]);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (keepPrevious && state.hasData && lastShown?.promise !== state.promise) setLastShown({
|
|
37
|
-
handle: query.handle,
|
|
38
|
-
hash: query.hash,
|
|
39
|
-
promise: state.promise,
|
|
40
|
-
updatedAt: state.updatedAt
|
|
41
|
-
});
|
|
42
|
-
const previous = keepPrevious && !state.hasData && state.status !== "error" && lastShown !== void 0 && lastShown.hash !== query.hash ? lastShown : void 0;
|
|
43
|
-
const shown = previous ?? {
|
|
44
|
-
handle: query.handle,
|
|
45
|
-
hash: query.hash,
|
|
46
|
-
promise: state.promise,
|
|
47
|
-
updatedAt: state.updatedAt
|
|
48
|
-
};
|
|
49
|
-
const controls = {
|
|
50
|
-
error: state.error,
|
|
51
|
-
fetchNextPage: query.fetchNextPage,
|
|
52
|
-
fetchPreviousPage: query.fetchPreviousPage,
|
|
53
|
-
hash: query.hash,
|
|
54
|
-
hasNextPage: query.hasNextPage(),
|
|
55
|
-
hasPreviousPage: query.hasPreviousPage(),
|
|
56
|
-
input: shown.handle.input,
|
|
57
|
-
isFetching: state.isFetching,
|
|
58
|
-
isFetchingNextPage: state.fetchDirection === "forward",
|
|
59
|
-
isFetchingPreviousPage: state.fetchDirection === "backward",
|
|
60
|
-
isPending: !state.hasData,
|
|
61
|
-
isPrevious: previous !== void 0,
|
|
62
|
-
isStale: state.isStale,
|
|
63
|
-
key: shown.handle.key,
|
|
64
|
-
refetch: query.refetch,
|
|
65
|
-
updatedAt: shown.updatedAt
|
|
66
|
-
};
|
|
67
|
-
if (handle.ensured === true) return unsafeCast([use(shown.promise), controls]);
|
|
68
|
-
return unsafeCast([shown.promise, controls]);
|
|
36
|
+
observer.trackProp("promise");
|
|
37
|
+
const controls = observer.trackResult(result);
|
|
38
|
+
if (handle.ensured === true) return unsafeCast([use(result.promise), controls]);
|
|
39
|
+
return unsafeCast([result.promise, controls]);
|
|
69
40
|
}
|
|
70
41
|
//#endregion
|
|
71
42
|
export { queryFor, useQuery };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raf-query/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
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.15",
|
|
44
44
|
"effect": "^4.0.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|