@veams/status-quo-query 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,25 @@ TanStack Query service helpers with a small subscribable surface that fits natur
8
8
  npm install @veams/status-quo-query @tanstack/query-core
9
9
  ```
10
10
 
11
+ React bindings are available through an optional peer dependency:
12
+
13
+ ```bash
14
+ npm install react
15
+ ```
16
+
17
+ ## Mental Model
18
+
19
+ Status Quo Query deliberately keeps the public surface small:
20
+
21
+ - `QueryService<TData, TError>` is the read handle for one query.
22
+ - `MutationService<TData, TError, TVariables>` is the write handle for one mutation.
23
+ - snapshots are passive state objects returned from `getSnapshot()` and `subscribe(...)`.
24
+ - commands stay on the handle: `refetch()`, `invalidate()`, `mutate()`, `reset()`.
25
+ - `QueryManager` is the broader coordination layer for cross-query work.
26
+ - `@veams/status-quo-query/react` is optional and adds one React subscription hook over the same handle shape.
27
+
28
+ That keeps the package usable in service code, state handlers, and React components without changing the core query or mutation API.
29
+
11
30
  ## Package Exports
12
31
 
13
32
  Root exports:
@@ -49,6 +68,7 @@ Subpath exports:
49
68
  - `@veams/status-quo-query/provider`
50
69
  - `@veams/status-quo-query/query`
51
70
  - `@veams/status-quo-query/mutation`
71
+ - `@veams/status-quo-query/react`
52
72
 
53
73
  ## Quickstart
54
74
 
@@ -109,6 +129,86 @@ await userQuery.refetch();
109
129
  await userQuery.invalidate({ refetchType: 'none' });
110
130
  ```
111
131
 
132
+ ## React Bindings
133
+
134
+ The React entrypoint exposes `useQuerySubscription(...)` and keeps `react` optional unless you
135
+ import `@veams/status-quo-query/react`.
136
+
137
+ ```tsx
138
+ import { useQuerySubscription } from '@veams/status-quo-query/react';
139
+ import type { QueryService } from '@veams/status-quo-query';
140
+
141
+ function ProductName({ query }: { query: QueryService<{ name: string }, Error> }) {
142
+ const snapshot = useQuerySubscription(query);
143
+
144
+ return <span>{snapshot.data?.name ?? 'loading'}</span>;
145
+ }
146
+ ```
147
+
148
+ Use the hook when a component should subscribe directly to a query service and render from its latest snapshot. Keep mapping at the component level:
149
+
150
+ - read `data`, `status`, `fetchStatus`, and flags like `isPending` from the snapshot
151
+ - call `query.refetch()` or `query.invalidate()` on the handle itself
152
+ - derive view-specific values in the component instead of adding selector logic to the hook
153
+
154
+ ## Status Quo Integration
155
+
156
+ The same query handle can also feed a `status-quo` handler through `bindSubscribable(...)`.
157
+
158
+ ```ts
159
+ import { NativeStateHandler } from '@veams/status-quo';
160
+ import {
161
+ toQueryMetaState,
162
+ type QueryMetaState,
163
+ type QueryService,
164
+ } from '@veams/status-quo-query';
165
+
166
+ type Product = {
167
+ id: string;
168
+ name: string;
169
+ };
170
+
171
+ type ProductCardState = {
172
+ product: Product | undefined;
173
+ query: QueryMetaState;
174
+ };
175
+
176
+ type ProductCardActions = {
177
+ refresh: () => Promise<void>;
178
+ };
179
+
180
+ export class ProductCardHandler extends NativeStateHandler<ProductCardState, ProductCardActions> {
181
+ constructor(private readonly productQuery: QueryService<Product, Error>) {
182
+ super({
183
+ initialState: {
184
+ product: productQuery.getSnapshot().data,
185
+ query: toQueryMetaState(productQuery.getSnapshot()),
186
+ },
187
+ });
188
+
189
+ this.bindSubscribable(productQuery, (snapshot) => {
190
+ this.setState(
191
+ {
192
+ product: snapshot.data,
193
+ query: toQueryMetaState(snapshot),
194
+ },
195
+ 'query:update'
196
+ );
197
+ });
198
+ }
199
+
200
+ getActions(): ProductCardActions {
201
+ return {
202
+ refresh: async () => {
203
+ await this.productQuery.refetch();
204
+ },
205
+ };
206
+ }
207
+ }
208
+ ```
209
+
210
+ Use that approach when query state is only one input into a broader UI state model and the handler should remain the view-facing boundary.
211
+
112
212
  ## Why Tracked Invalidation
113
213
 
114
214
  TanStack Query gives you flexible invalidation primitives, but the application still has to know which keys to invalidate after every mutation. Tracked invalidation moves that bookkeeping into the facade:
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { useQuerySubscription } from './use-query-subscription.js';
@@ -0,0 +1,2 @@
1
+ export { useQuerySubscription } from './use-query-subscription.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { QueryService, QueryServiceSnapshot } from '../../query.js';
2
+ export declare function useQuerySubscription<TData, TError>(queryService: QueryService<TData, TError>): QueryServiceSnapshot<TData, TError>;
@@ -0,0 +1,73 @@
1
+ // Import the React hooks needed to memoize callbacks, hold mutable cache state,
2
+ // and connect an external store to React rendering.
3
+ import { useCallback, useRef, useSyncExternalStore } from 'react';
4
+ // Subscribe a React component to a QueryService and return its latest snapshot.
5
+ export function useQuerySubscription(
6
+ // Receive the external query service instance to subscribe to.
7
+ queryService) {
8
+ // Count store notifications so we can tell when our cached snapshot is stale.
9
+ const snapshotVersionRef = useRef(0);
10
+ // Cache one client-side snapshot per observed version to keep getSnapshot stable.
11
+ const snapshotCacheRef = useRef(null);
12
+ // Cache the server snapshot separately for the useSyncExternalStore SSR fallback.
13
+ const serverSnapshotCacheRef = useRef(null);
14
+ // Create the subscribe function expected by useSyncExternalStore.
15
+ const subscribe = useCallback(
16
+ // React passes a listener that must run whenever the external store changes.
17
+ (listener) =>
18
+ // Forward the subscription to the query service.
19
+ queryService.subscribe(() => {
20
+ // Bump the version so later reads know the previous cache is outdated.
21
+ snapshotVersionRef.current += 1;
22
+ // Drop the cached client snapshot because the store just changed.
23
+ snapshotCacheRef.current = null;
24
+ // Drop the cached server snapshot for the same reason.
25
+ serverSnapshotCacheRef.current = null;
26
+ // Notify React that it should read a fresh snapshot.
27
+ listener();
28
+ }),
29
+ // Recreate the subscription function only when the service instance changes.
30
+ [queryService]);
31
+ // Read the current client snapshot in a referentially stable way for React.
32
+ const getSnapshot = useCallback(() => {
33
+ // Read the latest store version number.
34
+ const version = snapshotVersionRef.current;
35
+ // Read the last cached client snapshot, if there is one.
36
+ const cachedSnapshot = snapshotCacheRef.current;
37
+ // Reuse the cached snapshot when it was produced for the current version.
38
+ if (cachedSnapshot && cachedSnapshot.version === version) {
39
+ // Return the cached snapshot so repeated reads in the same render stay stable.
40
+ return cachedSnapshot.snapshot;
41
+ }
42
+ // Ask the query service for the latest snapshot because the cache is empty or stale.
43
+ const snapshot = queryService.getSnapshot();
44
+ // Store the new snapshot together with the version it belongs to.
45
+ snapshotCacheRef.current = {
46
+ snapshot,
47
+ version,
48
+ };
49
+ // Return the freshly read snapshot to React.
50
+ return snapshot;
51
+ // Recreate this getter only when the service instance changes.
52
+ }, [queryService]);
53
+ // Read the server snapshot used by React during SSR or hydration fallback paths.
54
+ const getServerSnapshot = useCallback(() => {
55
+ // Read the cached server snapshot, if one was stored earlier.
56
+ const cachedSnapshot = serverSnapshotCacheRef.current;
57
+ // Reuse the cached server snapshot to keep server reads stable.
58
+ if (cachedSnapshot) {
59
+ // Return the cached server snapshot directly.
60
+ return cachedSnapshot;
61
+ }
62
+ // Ask the query service for a snapshot because no server cache exists yet.
63
+ const snapshot = queryService.getSnapshot();
64
+ // Cache that snapshot for the next server read.
65
+ serverSnapshotCacheRef.current = snapshot;
66
+ // Return the freshly read server snapshot.
67
+ return snapshot;
68
+ // Recreate this getter only when the service instance changes.
69
+ }, [queryService]);
70
+ // Let React subscribe to the external store and read snapshots through the callbacks above.
71
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
72
+ }
73
+ //# sourceMappingURL=use-query-subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-query-subscription.js","sourceRoot":"","sources":["../../../src/react/hooks/use-query-subscription.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oDAAoD;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAelE,gFAAgF;AAChF,MAAM,UAAU,oBAAoB;AAClC,+DAA+D;AAC/D,YAAyC;IAEzC,8EAA8E;IAC9E,MAAM,kBAAkB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrC,kFAAkF;IAClF,MAAM,gBAAgB,GAAG,MAAM,CAA2C,IAAI,CAAC,CAAC;IAChF,kFAAkF;IAClF,MAAM,sBAAsB,GAAG,MAAM,CAA6C,IAAI,CAAC,CAAC;IAExF,kEAAkE;IAClE,MAAM,SAAS,GAAG,WAAW;IAC3B,6EAA6E;IAC7E,CAAC,QAAkB,EAAE,EAAE;IACrB,iDAAiD;IACjD,YAAY,CAAC,SAAS,CAAC,GAAG,EAAE;QAC1B,uEAAuE;QACvE,kBAAkB,CAAC,OAAO,IAAI,CAAC,CAAC;QAChC,kEAAkE;QAClE,gBAAgB,CAAC,OAAO,GAAG,IAAI,CAAC;QAChC,uDAAuD;QACvD,sBAAsB,CAAC,OAAO,GAAG,IAAI,CAAC;QACtC,qDAAqD;QACrD,QAAQ,EAAE,CAAC;IACb,CAAC,CAAC;IACJ,6EAA6E;IAC7E,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,4EAA4E;IAC5E,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,wCAAwC;QACxC,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC;QAC3C,yDAAyD;QACzD,MAAM,cAAc,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAEhD,0EAA0E;QAC1E,IAAI,cAAc,IAAI,cAAc,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YACzD,+EAA+E;YAC/E,OAAO,cAAc,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,qFAAqF;QACrF,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;QAC5C,kEAAkE;QAClE,gBAAgB,CAAC,OAAO,GAAG;YACzB,QAAQ;YACR,OAAO;SACR,CAAC;QAEF,6CAA6C;QAC7C,OAAO,QAAQ,CAAC;QAChB,+DAA+D;IACjE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,iFAAiF;IACjF,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;QACzC,8DAA8D;QAC9D,MAAM,cAAc,GAAG,sBAAsB,CAAC,OAAO,CAAC;QAEtD,gEAAgE;QAChE,IAAI,cAAc,EAAE,CAAC;YACnB,8CAA8C;YAC9C,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;QAC5C,gDAAgD;QAChD,sBAAsB,CAAC,OAAO,GAAG,QAAQ,CAAC;QAE1C,2CAA2C;QAC3C,OAAO,QAAQ,CAAC;QAChB,+DAA+D;IACjE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,4FAA4F;IAC5F,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1 @@
1
+ export { useQuerySubscription } from './hooks/index.js';
@@ -0,0 +1,2 @@
1
+ export { useQuerySubscription } from './hooks/index.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
package/eslint.config.mjs CHANGED
@@ -6,8 +6,8 @@ import globals from 'globals';
6
6
  import tseslint from 'typescript-eslint';
7
7
 
8
8
  const packageRoot = fileURLToPath(new URL('.', import.meta.url));
9
- const sourceFiles = ['src/**/*.ts'];
10
- const testFiles = ['src/**/*.{spec,test}.ts', 'src/**/__tests__/**/*.ts'];
9
+ const sourceFiles = ['src/**/*.{ts,tsx}'];
10
+ const testFiles = ['src/**/*.{spec,test}.{ts,tsx}', 'src/**/__tests__/**/*.{ts,tsx}'];
11
11
 
12
12
  export default defineConfig(
13
13
  {
@@ -20,9 +20,13 @@ export default defineConfig(
20
20
  files: sourceFiles,
21
21
  languageOptions: {
22
22
  globals: {
23
+ ...globals.browser,
23
24
  ...globals.node,
24
25
  },
25
26
  parserOptions: {
27
+ ecmaFeatures: {
28
+ jsx: true,
29
+ },
26
30
  project: './tsconfig.eslint.json',
27
31
  tsconfigRootDir: packageRoot,
28
32
  },
@@ -46,5 +50,12 @@ export default defineConfig(
46
50
  ...globals.jest,
47
51
  },
48
52
  },
53
+ rules: {
54
+ '@typescript-eslint/no-unsafe-argument': 'off',
55
+ '@typescript-eslint/no-unsafe-assignment': 'off',
56
+ '@typescript-eslint/no-unsafe-member-access': 'off',
57
+ '@typescript-eslint/no-unsafe-return': 'off',
58
+ '@typescript-eslint/require-await': 'off',
59
+ },
49
60
  }
50
61
  );
package/jest.config.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
- extensionsToTreatAsEsm: ['.ts'],
2
+ extensionsToTreatAsEsm: ['.ts', '.tsx'],
3
3
  transformIgnorePatterns: [],
4
4
  transform: {
5
5
  '^.+\\.(t|j)sx?$': '@swc/jest',
@@ -18,5 +18,5 @@ module.exports = {
18
18
  testMatch: ['**/__tests__/**/?(*.)+(spec|test).[jt]s?(x)'],
19
19
  reporters: ['default'],
20
20
  moduleFileExtensions: ['mjs', 'js', 'ts', 'jsx', 'tsx'],
21
- testEnvironment: 'node',
21
+ testEnvironment: 'jsdom',
22
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veams/status-quo-query",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "TanStack Query service layer for the VEAMS StatusQuo ecosystem.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -12,6 +12,13 @@
12
12
  },
13
13
  "require": "./dist/index.js"
14
14
  },
15
+ "./react": {
16
+ "import": {
17
+ "types": "./dist/react/index.d.ts",
18
+ "default": "./dist/react/index.js"
19
+ },
20
+ "require": "./dist/react/index.js"
21
+ },
15
22
  "./mutation": {
16
23
  "import": {
17
24
  "types": "./dist/mutation.d.ts",
@@ -50,12 +57,18 @@
50
57
  "check:types": "tsc --skipLibCheck --noEmit",
51
58
  "compile": "npm-run-all bundle:ts",
52
59
  "lint": "npm run lint:ts",
53
- "lint:ts": "eslint --fix \"src/**/*.ts\"",
60
+ "lint:ts": "eslint --fix \"src/**/*.{ts,tsx}\"",
54
61
  "test": "cross-env NODE_ENV=test jest --config jest.config.cjs",
55
62
  "release": "npm run build && release-it"
56
63
  },
57
64
  "peerDependencies": {
58
- "@tanstack/query-core": ">=5.0.0"
65
+ "@tanstack/query-core": ">=5.0.0",
66
+ "react": ">=18.0.0"
67
+ },
68
+ "peerDependenciesMeta": {
69
+ "react": {
70
+ "optional": true
71
+ }
59
72
  },
60
73
  "devDependencies": {
61
74
  "@swc/core": "1.15.11",
@@ -63,9 +76,14 @@
63
76
  "@tanstack/query-core": "5.91.2",
64
77
  "@types/jest": "30.0.0",
65
78
  "@types/node": "25.2.3",
79
+ "@types/react": "19.2.14",
80
+ "@types/react-dom": "19.0.0",
66
81
  "cross-env": "10.1.0",
67
82
  "jest": "30.3.0",
83
+ "jest-environment-jsdom": "30.3.0",
68
84
  "npm-run-all": "4.1.5",
85
+ "react": "19.2.4",
86
+ "react-dom": "19.2.4",
69
87
  "release-it": "19.2.4",
70
88
  "tslib": "2.8.1",
71
89
  "typescript": "5.9.3"
package/src/query.ts CHANGED
@@ -474,10 +474,6 @@ function bindQueryDependencies<
474
474
 
475
475
  function createDependencyController<
476
476
  TSources extends readonly unknown[] = [],
477
- TQueryFnData = unknown,
478
- TError = Error,
479
- TData = TQueryFnData,
480
- TQueryData = TQueryFnData,
481
477
  TQueryKey extends QueryKey = QueryKey,
482
478
  >(
483
479
  baseQueryKey: TQueryKey,
@@ -0,0 +1,104 @@
1
+ import React, { act } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { QueryClient } from '@tanstack/query-core';
4
+
5
+ import { setupQuery } from '../../query.js';
6
+ import { useQuerySubscription } from '../hooks/use-query-subscription.js';
7
+
8
+ import type { QueryService, QueryServiceSnapshot } from '../../query.js';
9
+
10
+ declare global {
11
+ var IS_REACT_ACT_ENVIRONMENT: boolean;
12
+ }
13
+
14
+ describe('useQuerySubscription', () => {
15
+ let container: HTMLDivElement;
16
+
17
+ beforeAll(() => {
18
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
19
+ });
20
+
21
+ beforeEach(() => {
22
+ container = document.createElement('div');
23
+ document.body.appendChild(container);
24
+ });
25
+
26
+ afterEach(() => {
27
+ container.remove();
28
+ });
29
+
30
+ it('renders the latest query snapshot', async () => {
31
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
32
+ const createQuery = setupQuery(queryClient);
33
+ const query = createQuery(['product', 1], jest.fn().mockResolvedValue({ name: 'Ada' }), {
34
+ enabled: false,
35
+ });
36
+ const renderStates: Array<string | undefined> = [];
37
+
38
+ const Consumer = () => {
39
+ const snapshot = useQuerySubscription(query);
40
+ renderStates.push(snapshot.data?.name);
41
+
42
+ return <span>{snapshot.data?.name ?? 'pending'}</span>;
43
+ };
44
+
45
+ const root = createRoot(container);
46
+
47
+ await act(async () => {
48
+ root.render(<Consumer />);
49
+ });
50
+
51
+ expect(container.textContent).toBe('pending');
52
+
53
+ await act(async () => {
54
+ await query.refetch();
55
+ });
56
+
57
+ expect(container.textContent).toBe('Ada');
58
+ expect(renderStates).toContain(undefined);
59
+ expect(renderStates).toContain('Ada');
60
+
61
+ await act(async () => {
62
+ root.unmount();
63
+ });
64
+ });
65
+ it('cleans up the store subscription on unmount', async () => {
66
+ const snapshot: QueryServiceSnapshot<{ name: string }, Error> = {
67
+ data: { name: 'Ada' },
68
+ error: null,
69
+ fetchStatus: 'idle',
70
+ status: 'success',
71
+ isError: false,
72
+ isFetching: false,
73
+ isPending: false,
74
+ isSuccess: true,
75
+ };
76
+ const unsubscribe = jest.fn();
77
+ const query: QueryService<{ name: string }, Error> = {
78
+ getSnapshot: () => snapshot,
79
+ subscribe: jest.fn(() => unsubscribe),
80
+ refetch: jest.fn(async () => snapshot),
81
+ invalidate: jest.fn(async () => undefined),
82
+ unsafe_getResult: jest.fn(),
83
+ };
84
+
85
+ const root = createRoot(container);
86
+
87
+ const Consumer = () => {
88
+ useQuerySubscription(query);
89
+ return <span>ready</span>;
90
+ };
91
+
92
+ await act(async () => {
93
+ root.render(<Consumer />);
94
+ });
95
+
96
+ expect(query.subscribe).toHaveBeenCalledTimes(1);
97
+
98
+ await act(async () => {
99
+ root.unmount();
100
+ });
101
+
102
+ expect(unsubscribe).toHaveBeenCalledTimes(1);
103
+ });
104
+ });
@@ -0,0 +1 @@
1
+ export { useQuerySubscription } from './use-query-subscription.js';
@@ -0,0 +1,98 @@
1
+ // Import the React hooks needed to memoize callbacks, hold mutable cache state,
2
+ // and connect an external store to React rendering.
3
+ import { useCallback, useRef, useSyncExternalStore } from 'react';
4
+
5
+ // Import the query service contract and the stable snapshot shape the hook returns.
6
+ import type { QueryService, QueryServiceSnapshot } from '../../query.js';
7
+ // Describe the no-argument listener shape expected by useSyncExternalStore.
8
+ type Listener = () => void;
9
+
10
+ // Store one cached snapshot together with the store version it belongs to.
11
+ type SnapshotCacheEntry<TData, TError> = {
12
+ // Hold the snapshot returned by the query service for this version.
13
+ snapshot: QueryServiceSnapshot<TData, TError>;
14
+ // Track which subscription version produced the cached snapshot.
15
+ version: number;
16
+ };
17
+
18
+ // Subscribe a React component to a QueryService and return its latest snapshot.
19
+ export function useQuerySubscription<TData, TError>(
20
+ // Receive the external query service instance to subscribe to.
21
+ queryService: QueryService<TData, TError>
22
+ ) {
23
+ // Count store notifications so we can tell when our cached snapshot is stale.
24
+ const snapshotVersionRef = useRef(0);
25
+ // Cache one client-side snapshot per observed version to keep getSnapshot stable.
26
+ const snapshotCacheRef = useRef<SnapshotCacheEntry<TData, TError> | null>(null);
27
+ // Cache the server snapshot separately for the useSyncExternalStore SSR fallback.
28
+ const serverSnapshotCacheRef = useRef<QueryServiceSnapshot<TData, TError> | null>(null);
29
+
30
+ // Create the subscribe function expected by useSyncExternalStore.
31
+ const subscribe = useCallback(
32
+ // React passes a listener that must run whenever the external store changes.
33
+ (listener: Listener) =>
34
+ // Forward the subscription to the query service.
35
+ queryService.subscribe(() => {
36
+ // Bump the version so later reads know the previous cache is outdated.
37
+ snapshotVersionRef.current += 1;
38
+ // Drop the cached client snapshot because the store just changed.
39
+ snapshotCacheRef.current = null;
40
+ // Drop the cached server snapshot for the same reason.
41
+ serverSnapshotCacheRef.current = null;
42
+ // Notify React that it should read a fresh snapshot.
43
+ listener();
44
+ }),
45
+ // Recreate the subscription function only when the service instance changes.
46
+ [queryService]
47
+ );
48
+
49
+ // Read the current client snapshot in a referentially stable way for React.
50
+ const getSnapshot = useCallback(() => {
51
+ // Read the latest store version number.
52
+ const version = snapshotVersionRef.current;
53
+ // Read the last cached client snapshot, if there is one.
54
+ const cachedSnapshot = snapshotCacheRef.current;
55
+
56
+ // Reuse the cached snapshot when it was produced for the current version.
57
+ if (cachedSnapshot && cachedSnapshot.version === version) {
58
+ // Return the cached snapshot so repeated reads in the same render stay stable.
59
+ return cachedSnapshot.snapshot;
60
+ }
61
+
62
+ // Ask the query service for the latest snapshot because the cache is empty or stale.
63
+ const snapshot = queryService.getSnapshot();
64
+ // Store the new snapshot together with the version it belongs to.
65
+ snapshotCacheRef.current = {
66
+ snapshot,
67
+ version,
68
+ };
69
+
70
+ // Return the freshly read snapshot to React.
71
+ return snapshot;
72
+ // Recreate this getter only when the service instance changes.
73
+ }, [queryService]);
74
+
75
+ // Read the server snapshot used by React during SSR or hydration fallback paths.
76
+ const getServerSnapshot = useCallback(() => {
77
+ // Read the cached server snapshot, if one was stored earlier.
78
+ const cachedSnapshot = serverSnapshotCacheRef.current;
79
+
80
+ // Reuse the cached server snapshot to keep server reads stable.
81
+ if (cachedSnapshot) {
82
+ // Return the cached server snapshot directly.
83
+ return cachedSnapshot;
84
+ }
85
+
86
+ // Ask the query service for a snapshot because no server cache exists yet.
87
+ const snapshot = queryService.getSnapshot();
88
+ // Cache that snapshot for the next server read.
89
+ serverSnapshotCacheRef.current = snapshot;
90
+
91
+ // Return the freshly read server snapshot.
92
+ return snapshot;
93
+ // Recreate this getter only when the service instance changes.
94
+ }, [queryService]);
95
+
96
+ // Let React subscribe to the external store and read snapshots through the callbacks above.
97
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
98
+ }
@@ -0,0 +1 @@
1
+ export { useQuerySubscription } from './hooks/index.js';
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
3
  "include": [
4
- "src/**/*.ts"
4
+ "src/**/*.ts",
5
+ "src/**/*.tsx"
5
6
  ],
6
7
  "exclude": [
7
8
  "coverage",
package/tsconfig.json CHANGED
@@ -4,8 +4,10 @@
4
4
  "declaration": true,
5
5
  "declarationDir": "dist",
6
6
  "esModuleInterop": true,
7
+ "jsx": "react",
7
8
  "lib": [
8
- "es2022"
9
+ "es2022",
10
+ "dom"
9
11
  ],
10
12
  "module": "es2022",
11
13
  "moduleResolution": "bundler",
@@ -19,7 +21,8 @@
19
21
  ]
20
22
  },
21
23
  "include": [
22
- "src/**/*.ts"
24
+ "src/**/*.ts",
25
+ "src/**/*.tsx"
23
26
  ],
24
27
  "exclude": [
25
28
  "coverage",