@ahoo-wang/fetcher-react 3.5.6 → 3.6.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 +869 -483
- package/README.zh-CN.md +2139 -605
- package/dist/core/debounced/index.d.ts +4 -0
- package/dist/core/debounced/index.d.ts.map +1 -0
- package/dist/core/debounced/useDebouncedCallback.d.ts.map +1 -0
- package/dist/core/{useDebouncedExecutePromise.d.ts → debounced/useDebouncedExecutePromise.d.ts} +1 -1
- package/dist/core/debounced/useDebouncedExecutePromise.d.ts.map +1 -0
- package/dist/{wow/debounce → core/debounced}/useDebouncedQuery.d.ts +1 -2
- package/dist/core/debounced/useDebouncedQuery.d.ts.map +1 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/{wow → core}/useQuery.d.ts +2 -2
- package/dist/core/useQuery.d.ts.map +1 -0
- package/dist/{wow → core}/useQueryState.d.ts +1 -1
- package/dist/core/useQueryState.d.ts.map +1 -0
- package/dist/cosec/RouteGuard.d.ts +49 -0
- package/dist/cosec/RouteGuard.d.ts.map +1 -0
- package/dist/cosec/SecurityContext.d.ts +47 -14
- package/dist/cosec/SecurityContext.d.ts.map +1 -1
- package/dist/cosec/index.d.ts +1 -0
- package/dist/cosec/index.d.ts.map +1 -1
- package/dist/cosec/useSecurity.d.ts +60 -8
- package/dist/cosec/useSecurity.d.ts.map +1 -1
- package/dist/fetcher/debounced/index.d.ts +3 -0
- package/dist/fetcher/debounced/index.d.ts.map +1 -0
- package/dist/fetcher/{useDebouncedFetcher.d.ts → debounced/useDebouncedFetcher.d.ts} +2 -2
- package/dist/fetcher/debounced/useDebouncedFetcher.d.ts.map +1 -0
- package/dist/{wow/debounce → fetcher/debounced}/useDebouncedFetcherQuery.d.ts +1 -1
- package/dist/fetcher/debounced/useDebouncedFetcherQuery.d.ts.map +1 -0
- package/dist/fetcher/index.d.ts +2 -1
- package/dist/fetcher/index.d.ts.map +1 -1
- package/dist/{wow → fetcher}/useFetcherQuery.d.ts +3 -3
- package/dist/fetcher/useFetcherQuery.d.ts.map +1 -0
- package/dist/index.es.js +567 -538
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wow/fetcher/index.d.ts +6 -0
- package/dist/wow/fetcher/index.d.ts.map +1 -0
- package/dist/wow/{useFetcherCountQuery.d.ts → fetcher/useFetcherCountQuery.d.ts} +2 -2
- package/dist/wow/fetcher/useFetcherCountQuery.d.ts.map +1 -0
- package/dist/wow/{useFetcherListQuery.d.ts → fetcher/useFetcherListQuery.d.ts} +2 -2
- package/dist/wow/fetcher/useFetcherListQuery.d.ts.map +1 -0
- package/dist/wow/{useFetcherListStreamQuery.d.ts → fetcher/useFetcherListStreamQuery.d.ts} +2 -2
- package/dist/wow/fetcher/useFetcherListStreamQuery.d.ts.map +1 -0
- package/dist/wow/{useFetcherPagedQuery.d.ts → fetcher/useFetcherPagedQuery.d.ts} +2 -2
- package/dist/wow/fetcher/useFetcherPagedQuery.d.ts.map +1 -0
- package/dist/wow/{useFetcherSingleQuery.d.ts → fetcher/useFetcherSingleQuery.d.ts} +2 -2
- package/dist/wow/fetcher/useFetcherSingleQuery.d.ts.map +1 -0
- package/dist/wow/index.d.ts +1 -10
- package/dist/wow/index.d.ts.map +1 -1
- package/dist/wow/useCountQuery.d.ts +1 -1
- package/dist/wow/useCountQuery.d.ts.map +1 -1
- package/dist/wow/useListQuery.d.ts +1 -1
- package/dist/wow/useListQuery.d.ts.map +1 -1
- package/dist/wow/useListStreamQuery.d.ts +1 -1
- package/dist/wow/useListStreamQuery.d.ts.map +1 -1
- package/dist/wow/usePagedQuery.d.ts +1 -1
- package/dist/wow/usePagedQuery.d.ts.map +1 -1
- package/dist/wow/useSingleQuery.d.ts +1 -1
- package/dist/wow/useSingleQuery.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/core/useDebouncedCallback.d.ts.map +0 -1
- package/dist/core/useDebouncedExecutePromise.d.ts.map +0 -1
- package/dist/fetcher/useDebouncedFetcher.d.ts.map +0 -1
- package/dist/wow/debounce/index.d.ts +0 -3
- package/dist/wow/debounce/index.d.ts.map +0 -1
- package/dist/wow/debounce/useDebouncedFetcherQuery.d.ts.map +0 -1
- package/dist/wow/debounce/useDebouncedQuery.d.ts.map +0 -1
- package/dist/wow/types.d.ts +0 -7
- package/dist/wow/types.d.ts.map +0 -1
- package/dist/wow/useFetcherCountQuery.d.ts.map +0 -1
- package/dist/wow/useFetcherListQuery.d.ts.map +0 -1
- package/dist/wow/useFetcherListStreamQuery.d.ts.map +0 -1
- package/dist/wow/useFetcherPagedQuery.d.ts.map +0 -1
- package/dist/wow/useFetcherQuery.d.ts.map +0 -1
- package/dist/wow/useFetcherSingleQuery.d.ts.map +0 -1
- package/dist/wow/useQuery.d.ts.map +0 -1
- package/dist/wow/useQueryState.d.ts.map +0 -1
- /package/dist/core/{useDebouncedCallback.d.ts → debounced/useDebouncedCallback.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -29,37 +29,47 @@ robust data fetching capabilities.
|
|
|
29
29
|
- [Quick Start](#quick-start)
|
|
30
30
|
- [Usage](#usage)
|
|
31
31
|
- [Core Hooks](#core-hooks)
|
|
32
|
-
- [useFetcher](#usefetcher-hook)
|
|
33
32
|
- [useExecutePromise](#useexecutepromise-hook)
|
|
34
33
|
- [usePromiseState](#usepromisestate-hook)
|
|
35
|
-
- [Debounced Hooks](#debounced-hooks)
|
|
36
|
-
- [useDebouncedCallback](#usedebouncedcallback)
|
|
37
|
-
- [useDebouncedExecutePromise](#usedebouncedexecutepromise)
|
|
38
|
-
- [useDebouncedFetcher](#usedebouncedfetcher)
|
|
39
|
-
- [useDebouncedFetcherQuery](#usedebouncedfetcherquery)
|
|
40
|
-
- [useDebouncedQuery](#usedebouncedquery)
|
|
41
|
-
- [Utility Hooks](#utility-hooks)
|
|
42
34
|
- [useRequestId](#userequestid-hook)
|
|
43
35
|
- [useLatest](#uselatest-hook)
|
|
44
36
|
- [useRefs](#userefs-hook)
|
|
37
|
+
- [useQuery](#usequery-hook)
|
|
38
|
+
- [useQueryState](#usequerystate-hook)
|
|
39
|
+
- [useMounted](#usemounted-hook)
|
|
40
|
+
- [useForceUpdate](#useforceupdate-hook)
|
|
41
|
+
- [Debounced Hooks](#debounced-hooks)
|
|
42
|
+
- [useDebouncedCallback](#usedebouncedcallback)
|
|
43
|
+
- [useDebouncedExecutePromise](#usedebouncedexecutepromise)
|
|
44
|
+
- [useDebouncedQuery](#usedebouncedquery)
|
|
45
|
+
- [Fetcher Hooks](#fetcher-hooks)
|
|
46
|
+
- [useFetcher](#usefetcher-hook)
|
|
47
|
+
- [useFetcherQuery](#usefetcherquery-hook)
|
|
48
|
+
- [Debounced Fetcher Hooks](#debounced-fetcher-hooks)
|
|
49
|
+
- [useDebouncedFetcher](#usedebouncedfetcher)
|
|
50
|
+
- [useDebouncedFetcherQuery](#usedebouncedfetcherquery)
|
|
45
51
|
- [Storage Hooks](#storage-hooks)
|
|
46
52
|
- [useKeyStorage](#usekeystorage-hook)
|
|
47
53
|
- [useImmerKeyStorage](#useimmerkeystorage-hook)
|
|
48
54
|
- [Event Hooks](#event-hooks)
|
|
49
55
|
- [useEventSubscription](#useeventsubscription-hook)
|
|
56
|
+
- [CoSec Security Hooks](#cosec-security-hooks)
|
|
57
|
+
- [useSecurity](#usesecurity-hook)
|
|
58
|
+
- [SecurityProvider](#securityprovider)
|
|
59
|
+
- [useSecurityContext](#usesecuritycontext-hook)
|
|
60
|
+
- [RouteGuard](#routeguard)
|
|
50
61
|
- [Wow Query Hooks](#wow-query-hooks)
|
|
51
62
|
- [Basic Query Hooks](#basic-query-hooks)
|
|
52
63
|
- [useListQuery](#uselistquery-hook)
|
|
53
64
|
- [usePagedQuery](#usepagedquery-hook)
|
|
54
65
|
- [useSingleQuery](#usesinglequery-hook)
|
|
55
66
|
- [useCountQuery](#usecountquery-hook)
|
|
67
|
+
- [useListStreamQuery](#useliststreamquery-hook)
|
|
56
68
|
- [Fetcher Query Hooks](#fetcher-query-hooks)
|
|
57
69
|
- [useFetcherListQuery](#usefetcherlistquery-hook)
|
|
58
70
|
- [useFetcherPagedQuery](#usefetcherpagedquery-hook)
|
|
59
71
|
- [useFetcherSingleQuery](#usefetchersinglequery-hook)
|
|
60
72
|
- [useFetcherCountQuery](#usefetchercountquery-hook)
|
|
61
|
-
- [Stream Query Hooks](#stream-query-hooks)
|
|
62
|
-
- [useListStreamQuery](#useliststreamquery-hook)
|
|
63
73
|
- [useFetcherListStreamQuery](#usefetcherliststreamquery-hook)
|
|
64
74
|
- [Best Practices](#best-practices)
|
|
65
75
|
- [API Reference](#api-reference)
|
|
@@ -103,270 +113,429 @@ function App() {
|
|
|
103
113
|
|
|
104
114
|
### Core Hooks
|
|
105
115
|
|
|
106
|
-
####
|
|
116
|
+
#### useExecutePromise Hook
|
|
107
117
|
|
|
108
|
-
The `
|
|
109
|
-
protection, and
|
|
118
|
+
The `useExecutePromise` hook manages asynchronous operations with automatic state handling, built-in race condition
|
|
119
|
+
protection, and support for promise state options. It includes automatic AbortController support for canceling operations.
|
|
110
120
|
|
|
111
121
|
```typescript jsx
|
|
112
|
-
import {
|
|
122
|
+
import { useExecutePromise } from '@ahoo-wang/fetcher-react';
|
|
113
123
|
|
|
114
124
|
const MyComponent = () => {
|
|
115
|
-
const { loading,
|
|
125
|
+
const { loading, result, error, execute, reset, abort } = useExecutePromise<string>({
|
|
116
126
|
onAbort: () => {
|
|
117
|
-
console.log('
|
|
127
|
+
console.log('Operation was aborted');
|
|
118
128
|
}
|
|
119
129
|
});
|
|
120
130
|
|
|
131
|
+
const fetchData = async () => {
|
|
132
|
+
const response = await fetch('/api/data');
|
|
133
|
+
return response.text();
|
|
134
|
+
};
|
|
135
|
+
|
|
121
136
|
const handleFetch = () => {
|
|
122
|
-
execute(
|
|
137
|
+
execute(fetchData); // Using a promise supplier
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const handleDirectPromise = () => {
|
|
141
|
+
const promise = fetch('/api/data').then(res => res.text());
|
|
142
|
+
execute(promise); // Using a direct promise
|
|
123
143
|
};
|
|
124
144
|
|
|
125
145
|
const handleAbort = () => {
|
|
126
|
-
abort(); //
|
|
146
|
+
abort(); // Manually abort the current operation
|
|
127
147
|
};
|
|
148
|
+
|
|
149
|
+
if (loading) return <div>Loading...</div>;
|
|
150
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
151
|
+
return (
|
|
152
|
+
<div>
|
|
153
|
+
<button onClick={handleFetch}>Fetch with Supplier</button>
|
|
154
|
+
<button onClick={handleDirectPromise}>Fetch with Promise</button>
|
|
155
|
+
<button onClick={handleAbort} disabled={!loading}>Abort</button>
|
|
156
|
+
<button onClick={reset}>Reset</button>
|
|
157
|
+
{result && <p>{result}</p>}
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
128
161
|
```
|
|
129
162
|
|
|
130
|
-
|
|
163
|
+
##### Abort Controller Support
|
|
164
|
+
|
|
165
|
+
The hook automatically creates an AbortController for each operation and provides methods to manage cancellation:
|
|
166
|
+
|
|
167
|
+
- **Automatic Cleanup**: Operations are automatically aborted when the component unmounts
|
|
168
|
+
- **Manual Abort**: Use the `abort()` method to cancel ongoing operations
|
|
169
|
+
- **onAbort Callback**: Configure a callback that fires when an operation is aborted (manually or automatically)
|
|
170
|
+
- **AbortController Access**: The AbortController is passed to promise suppliers for advanced cancellation handling
|
|
171
|
+
|
|
172
|
+
#### usePromiseState Hook
|
|
173
|
+
|
|
174
|
+
The `usePromiseState` hook provides state management for promise operations without execution logic. Supports both
|
|
175
|
+
static options and dynamic option suppliers.
|
|
131
176
|
|
|
132
177
|
```typescript jsx
|
|
133
|
-
import {
|
|
178
|
+
import { usePromiseState, PromiseStatus } from '@ahoo-wang/fetcher-react';
|
|
134
179
|
|
|
135
180
|
const MyComponent = () => {
|
|
136
|
-
const {
|
|
137
|
-
initialQuery: { condition: {}, projection: {}, sort: [], limit: 10 },
|
|
138
|
-
list: async (listQuery) => fetchListData(listQuery),
|
|
139
|
-
autoExecute: true, // Automatically execute on component mount
|
|
140
|
-
});
|
|
181
|
+
const { status, loading, result, error, setSuccess, setError, setIdle } = usePromiseState<string>();
|
|
141
182
|
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
const handleSuccess = () => setSuccess('Data loaded');
|
|
184
|
+
const handleError = () => setError(new Error('Failed to load'));
|
|
144
185
|
|
|
145
|
-
|
|
146
|
-
|
|
186
|
+
return (
|
|
187
|
+
<div>
|
|
188
|
+
<button onClick={handleSuccess}>Set Success</button>
|
|
189
|
+
<button onClick={handleError}>Set Error</button>
|
|
190
|
+
<button onClick={setIdle}>Reset</button>
|
|
191
|
+
<p>Status: {status}</p>
|
|
192
|
+
{loading && <p>Loading...</p>}
|
|
193
|
+
{result && <p>Result: {result}</p>}
|
|
194
|
+
{error && <p>Error: {error.message}</p>}
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
##### usePromiseState with Options Supplier
|
|
201
|
+
|
|
202
|
+
```typescript jsx
|
|
203
|
+
import { usePromiseState, PromiseStatus } from '@ahoo-wang/fetcher-react';
|
|
204
|
+
|
|
205
|
+
const MyComponent = () => {
|
|
206
|
+
// Using options supplier for dynamic configuration
|
|
207
|
+
const optionsSupplier = () => ({
|
|
208
|
+
initialStatus: PromiseStatus.IDLE,
|
|
209
|
+
onSuccess: async (result: string) => {
|
|
210
|
+
await saveToAnalytics(result);
|
|
211
|
+
console.log('Success:', result);
|
|
212
|
+
},
|
|
213
|
+
onError: async (error) => {
|
|
214
|
+
await logErrorToServer(error);
|
|
215
|
+
console.error('Error:', error);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const { setSuccess, setError } = usePromiseState<string>(optionsSupplier);
|
|
147
220
|
|
|
148
221
|
return (
|
|
149
222
|
<div>
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
<li key={index}>{item.name}</li>
|
|
153
|
-
))}
|
|
154
|
-
</ul>
|
|
223
|
+
<button onClick={() => setSuccess('Dynamic success!')}>Set Success</button>
|
|
224
|
+
<button onClick={() => setError(new Error('Dynamic error!'))}>Set Error</button>
|
|
155
225
|
</div>
|
|
156
226
|
);
|
|
157
227
|
};
|
|
158
228
|
```
|
|
159
229
|
|
|
160
|
-
|
|
230
|
+
#### useRequestId Hook
|
|
161
231
|
|
|
162
|
-
|
|
232
|
+
The `useRequestId` hook provides request ID management for preventing race conditions in async operations.
|
|
163
233
|
|
|
164
|
-
|
|
234
|
+
```typescript jsx
|
|
235
|
+
import { useRequestId } from '@ahoo-wang/fetcher-react';
|
|
165
236
|
|
|
166
|
-
|
|
237
|
+
const MyComponent = () => {
|
|
238
|
+
const { generate, isLatest, invalidate } = useRequestId();
|
|
239
|
+
|
|
240
|
+
const handleFetch = async () => {
|
|
241
|
+
const requestId = generate();
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const result = await fetchData();
|
|
245
|
+
|
|
246
|
+
if (isLatest(requestId)) {
|
|
247
|
+
setData(result);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (isLatest(requestId)) {
|
|
251
|
+
setError(error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<div>
|
|
258
|
+
<button onClick={handleFetch}>Fetch Data</button>
|
|
259
|
+
<button onClick={invalidate}>Cancel Ongoing</button>
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### useLatest Hook
|
|
266
|
+
|
|
267
|
+
The `useLatest` hook returns a ref containing the latest value, useful for accessing the current value in async
|
|
268
|
+
callbacks.
|
|
167
269
|
|
|
168
270
|
```typescript jsx
|
|
169
|
-
import {
|
|
271
|
+
import { useLatest } from '@ahoo-wang/fetcher-react';
|
|
170
272
|
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
273
|
+
const MyComponent = () => {
|
|
274
|
+
const [count, setCount] = useState(0);
|
|
275
|
+
const latestCount = useLatest(count);
|
|
276
|
+
|
|
277
|
+
const handleAsync = async () => {
|
|
278
|
+
await someAsyncOperation();
|
|
279
|
+
console.log('Latest count:', latestCount.current); // Always the latest
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<div>
|
|
284
|
+
<p>Count: {count}</p>
|
|
285
|
+
<button onClick={() => setCount(c => c + 1)}>Increment</button>
|
|
286
|
+
<button onClick={handleAsync}>Async Log</button>
|
|
287
|
+
</div>
|
|
179
288
|
);
|
|
289
|
+
};
|
|
290
|
+
```
|
|
180
291
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
292
|
+
#### useRefs Hook
|
|
293
|
+
|
|
294
|
+
The `useRefs` hook provides a Map-like interface for managing multiple React refs dynamically. It allows registering, retrieving, and managing refs by key, with automatic cleanup on component unmount.
|
|
295
|
+
|
|
296
|
+
```typescript jsx
|
|
297
|
+
import { useRefs } from '@ahoo-wang/fetcher-react';
|
|
298
|
+
|
|
299
|
+
const MyComponent = () => {
|
|
300
|
+
const refs = useRefs<HTMLDivElement>();
|
|
301
|
+
|
|
302
|
+
const handleFocus = (key: string) => {
|
|
303
|
+
const element = refs.get(key);
|
|
304
|
+
element?.focus();
|
|
187
305
|
};
|
|
188
306
|
|
|
189
307
|
return (
|
|
190
308
|
<div>
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/>
|
|
196
|
-
{isPending() && <div>Searching...</div>}
|
|
309
|
+
<div ref={refs.register('first')} tabIndex={0}>First Element</div>
|
|
310
|
+
<div ref={refs.register('second')} tabIndex={0}>Second Element</div>
|
|
311
|
+
<button onClick={() => handleFocus('first')}>Focus First</button>
|
|
312
|
+
<button onClick={() => handleFocus('second')}>Focus Second</button>
|
|
197
313
|
</div>
|
|
198
314
|
);
|
|
199
315
|
};
|
|
200
316
|
```
|
|
201
317
|
|
|
202
|
-
|
|
318
|
+
Key features:
|
|
203
319
|
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
320
|
+
- **Dynamic Registration**: Register refs with string, number, or symbol keys
|
|
321
|
+
- **Map-like API**: Full Map interface with get, set, has, delete, etc.
|
|
322
|
+
- **Automatic Cleanup**: Refs are cleared when component unmounts
|
|
323
|
+
- **Type Safety**: Full TypeScript support for ref types
|
|
207
324
|
|
|
208
|
-
####
|
|
325
|
+
#### useQuery Hook
|
|
209
326
|
|
|
210
|
-
|
|
327
|
+
The `useQuery` hook provides a complete solution for managing query-based asynchronous operations with automatic state management and execution control.
|
|
211
328
|
|
|
212
329
|
```typescript jsx
|
|
213
|
-
import {
|
|
330
|
+
import { useQuery } from '@ahoo-wang/fetcher-react';
|
|
214
331
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
332
|
+
interface UserQuery {
|
|
333
|
+
id: string;
|
|
334
|
+
}
|
|
219
335
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
336
|
+
interface User {
|
|
337
|
+
id: string;
|
|
338
|
+
name: string;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function UserComponent() {
|
|
342
|
+
const { loading, result, error, execute, setQuery } = useQuery<UserQuery, User>({
|
|
343
|
+
initialQuery: { id: '1' },
|
|
344
|
+
execute: async (query) => {
|
|
345
|
+
const response = await fetch(`/api/users/${query.id}`);
|
|
223
346
|
return response.json();
|
|
224
|
-
}
|
|
347
|
+
},
|
|
348
|
+
autoExecute: true,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const handleUserChange = (userId: string) => {
|
|
352
|
+
setQuery({ id: userId }); // Automatically executes if autoExecute is true
|
|
225
353
|
};
|
|
226
354
|
|
|
355
|
+
if (loading) return <div>Loading...</div>;
|
|
356
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
227
357
|
return (
|
|
228
358
|
<div>
|
|
229
|
-
<button onClick={() =>
|
|
230
|
-
|
|
359
|
+
<button onClick={() => handleUserChange('2')}>Load User 2</button>
|
|
360
|
+
{result && <p>User: {result.name}</p>}
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### useQueryState Hook
|
|
367
|
+
|
|
368
|
+
The `useQueryState` hook provides state management for query parameters with automatic execution capabilities.
|
|
369
|
+
|
|
370
|
+
```typescript jsx
|
|
371
|
+
import { useQueryState } from '@ahoo-wang/fetcher-react';
|
|
372
|
+
|
|
373
|
+
interface UserQuery {
|
|
374
|
+
id: string;
|
|
375
|
+
name?: string;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function UserComponent() {
|
|
379
|
+
const executeQuery = async (query: UserQuery) => {
|
|
380
|
+
// Perform query execution logic here
|
|
381
|
+
console.log('Executing query:', query);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const { getQuery, setQuery } = useQueryState<UserQuery>({
|
|
385
|
+
initialQuery: { id: '1' },
|
|
386
|
+
autoExecute: true,
|
|
387
|
+
execute: executeQuery,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const handleQueryChange = (newQuery: UserQuery) => {
|
|
391
|
+
setQuery(newQuery); // Will automatically execute if autoExecute is true
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const currentQuery = getQuery(); // Get current query parameters
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
<div>
|
|
398
|
+
<button onClick={() => handleQueryChange({ id: '2', name: 'John' })}>
|
|
399
|
+
Update Query
|
|
231
400
|
</button>
|
|
232
|
-
{loading && <div>Loading...</div>}
|
|
233
|
-
{error && <div>Error: {error.message}</div>}
|
|
234
|
-
{result && <div>User: {result.name}</div>}
|
|
235
401
|
</div>
|
|
236
402
|
);
|
|
237
|
-
}
|
|
403
|
+
}
|
|
238
404
|
```
|
|
239
405
|
|
|
240
|
-
####
|
|
406
|
+
#### useMounted Hook
|
|
241
407
|
|
|
242
|
-
|
|
408
|
+
The `useMounted` hook provides a way to check if a component is still mounted, useful for avoiding state updates on unmounted components.
|
|
243
409
|
|
|
244
410
|
```typescript jsx
|
|
245
|
-
import {
|
|
411
|
+
import { useMounted } from '@ahoo-wang/fetcher-react';
|
|
246
412
|
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
413
|
+
const MyComponent = () => {
|
|
414
|
+
const isMounted = useMounted();
|
|
415
|
+
|
|
416
|
+
const handleAsyncOperation = async () => {
|
|
417
|
+
const result = await someAsyncOperation();
|
|
418
|
+
|
|
419
|
+
// Check if component is still mounted before updating state
|
|
420
|
+
if (isMounted()) {
|
|
421
|
+
setData(result);
|
|
253
422
|
}
|
|
254
|
-
}
|
|
423
|
+
};
|
|
255
424
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
425
|
+
return (
|
|
426
|
+
<div>
|
|
427
|
+
<button onClick={handleAsyncOperation}>Perform Async Operation</button>
|
|
428
|
+
</div>
|
|
429
|
+
);
|
|
430
|
+
};
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
#### useForceUpdate Hook
|
|
434
|
+
|
|
435
|
+
The `useForceUpdate` hook provides a way to force a component to re-render, useful when you need to trigger a render based on external changes.
|
|
436
|
+
|
|
437
|
+
```typescript jsx
|
|
438
|
+
import { useForceUpdate } from '@ahoo-wang/fetcher-react';
|
|
439
|
+
|
|
440
|
+
const MyComponent = () => {
|
|
441
|
+
const forceUpdate = useForceUpdate();
|
|
442
|
+
|
|
443
|
+
const handleExternalChange = () => {
|
|
444
|
+
// Perform some external operation that doesn't trigger a re-render
|
|
445
|
+
updateExternalState();
|
|
446
|
+
|
|
447
|
+
// Force the component to re-render to reflect the changes
|
|
448
|
+
forceUpdate();
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
return (
|
|
452
|
+
<div>
|
|
453
|
+
<button onClick={handleExternalChange}>Force Update</button>
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
};
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Debounced Hooks
|
|
460
|
+
|
|
461
|
+
🚀 **Advanced Debouncing for React Applications** - Powerful hooks that combine debouncing with async operations, providing seamless rate limiting for API calls, user interactions, and promise execution.
|
|
462
|
+
|
|
463
|
+
#### useDebouncedCallback
|
|
464
|
+
|
|
465
|
+
A React hook that provides a debounced version of any callback function with leading/trailing edge execution options.
|
|
466
|
+
|
|
467
|
+
```typescript jsx
|
|
468
|
+
import { useDebouncedCallback } from '@ahoo-wang/fetcher-react';
|
|
469
|
+
|
|
470
|
+
const SearchComponent = () => {
|
|
471
|
+
const { run: debouncedSearch, cancel, isPending } = useDebouncedCallback(
|
|
472
|
+
async (query: string) => {
|
|
473
|
+
const response = await fetch(`/api/search?q=${query}`);
|
|
474
|
+
const results = await response.json();
|
|
475
|
+
console.log('Search results:', results);
|
|
476
|
+
},
|
|
477
|
+
{ delay: 300 }
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
const handleSearch = (query: string) => {
|
|
481
|
+
if (query.trim()) {
|
|
482
|
+
debouncedSearch(query);
|
|
483
|
+
} else {
|
|
484
|
+
cancel(); // Cancel any pending search
|
|
264
485
|
}
|
|
265
486
|
};
|
|
266
487
|
|
|
267
488
|
return (
|
|
268
489
|
<div>
|
|
269
490
|
<input
|
|
270
|
-
|
|
271
|
-
onChange={(e) => handleChange(e.target.value)}
|
|
491
|
+
type="text"
|
|
272
492
|
placeholder="Search..."
|
|
493
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
273
494
|
/>
|
|
274
|
-
{
|
|
275
|
-
{error && <div>Error: {error.message}</div>}
|
|
276
|
-
{result && <SearchResults data={result} />}
|
|
495
|
+
{isPending() && <div>Searching...</div>}
|
|
277
496
|
</div>
|
|
278
497
|
);
|
|
279
498
|
};
|
|
280
499
|
```
|
|
281
500
|
|
|
282
|
-
**
|
|
501
|
+
**Configuration Options:**
|
|
283
502
|
|
|
284
|
-
-
|
|
285
|
-
-
|
|
286
|
-
-
|
|
503
|
+
- `delay`: Delay in milliseconds before execution (required, positive number)
|
|
504
|
+
- `leading`: Execute immediately on first call (default: false)
|
|
505
|
+
- `trailing`: Execute after delay on last call (default: true)
|
|
287
506
|
|
|
288
|
-
####
|
|
507
|
+
#### useDebouncedExecutePromise
|
|
289
508
|
|
|
290
|
-
Combines
|
|
509
|
+
Combines promise execution with debouncing functionality, perfect for API calls and async operations.
|
|
291
510
|
|
|
292
511
|
```typescript jsx
|
|
293
|
-
import {
|
|
294
|
-
|
|
295
|
-
interface SearchQuery {
|
|
296
|
-
keyword: string;
|
|
297
|
-
limit: number;
|
|
298
|
-
filters?: { category?: string };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
interface SearchResult {
|
|
302
|
-
items: Array<{ id: string; title: string }>;
|
|
303
|
-
total: number;
|
|
304
|
-
}
|
|
512
|
+
import { useDebouncedExecutePromise } from '@ahoo-wang/fetcher-react';
|
|
305
513
|
|
|
306
|
-
const
|
|
307
|
-
const {
|
|
308
|
-
|
|
309
|
-
result,
|
|
310
|
-
error,
|
|
311
|
-
run,
|
|
312
|
-
cancel,
|
|
313
|
-
isPending,
|
|
314
|
-
setQuery,
|
|
315
|
-
getQuery,
|
|
316
|
-
} = useDebouncedFetcherQuery<SearchQuery, SearchResult>({
|
|
317
|
-
url: '/api/search',
|
|
318
|
-
initialQuery: { keyword: '', limit: 10 },
|
|
319
|
-
debounce: { delay: 300 }, // Debounce for 300ms
|
|
320
|
-
autoExecute: false, // Don't execute on mount
|
|
514
|
+
const DataFetcher = () => {
|
|
515
|
+
const { loading, result, error, run } = useDebouncedExecutePromise({
|
|
516
|
+
debounce: { delay: 300 },
|
|
321
517
|
});
|
|
322
518
|
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
run(); // Manual debounced execution with current query
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const handleCancel = () => {
|
|
332
|
-
cancel(); // Cancel any pending debounced execution
|
|
519
|
+
const handleLoadUser = (userId: string) => {
|
|
520
|
+
run(async () => {
|
|
521
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
522
|
+
return response.json();
|
|
523
|
+
});
|
|
333
524
|
};
|
|
334
525
|
|
|
335
|
-
if (loading) return <div>Searching...</div>;
|
|
336
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
337
|
-
|
|
338
526
|
return (
|
|
339
527
|
<div>
|
|
340
|
-
<
|
|
341
|
-
|
|
342
|
-
onChange={(e) => handleSearch(e.target.value)}
|
|
343
|
-
placeholder="Search..."
|
|
344
|
-
/>
|
|
345
|
-
<button onClick={handleManualSearch} disabled={isPending()}>
|
|
346
|
-
{isPending() ? 'Searching...' : 'Search'}
|
|
528
|
+
<button onClick={() => handleLoadUser('user123')}>
|
|
529
|
+
Load User
|
|
347
530
|
</button>
|
|
348
|
-
<
|
|
349
|
-
{
|
|
350
|
-
|
|
351
|
-
Found {result.total} items:
|
|
352
|
-
{result.items.map(item => (
|
|
353
|
-
<div key={item.id}>{item.title}</div>
|
|
354
|
-
))}
|
|
355
|
-
</div>
|
|
356
|
-
)}
|
|
531
|
+
{loading && <div>Loading...</div>}
|
|
532
|
+
{error && <div>Error: {error.message}</div>}
|
|
533
|
+
{result && <div>User: {result.name}</div>}
|
|
357
534
|
</div>
|
|
358
535
|
);
|
|
359
536
|
};
|
|
360
537
|
```
|
|
361
538
|
|
|
362
|
-
**Key Features:**
|
|
363
|
-
|
|
364
|
-
- **Query State Management**: Automatic query parameter handling with `setQuery` and `getQuery`
|
|
365
|
-
- **Debounced Execution**: Prevents excessive API calls during rapid user input
|
|
366
|
-
- **Auto-Execution**: Optional automatic execution when query parameters change
|
|
367
|
-
- **Manual Control**: `run()` for manual execution, `cancel()` for cancellation
|
|
368
|
-
- **Pending State**: `isPending()` to check if a debounced call is queued
|
|
369
|
-
|
|
370
539
|
#### useDebouncedQuery
|
|
371
540
|
|
|
372
541
|
Combines general query execution with debouncing, perfect for custom query operations where you want to debounce execution based on query parameters.
|
|
@@ -457,259 +626,222 @@ const SearchComponent = () => {
|
|
|
457
626
|
- **Pending State**: `isPending()` to check if a debounced call is queued
|
|
458
627
|
- **Custom Execution**: Flexible execute function for any query operation
|
|
459
628
|
|
|
460
|
-
###
|
|
629
|
+
### Fetcher Hooks
|
|
461
630
|
|
|
462
|
-
|
|
463
|
-
|
|
631
|
+
#### useFetcher Hook
|
|
632
|
+
|
|
633
|
+
The `useFetcher` hook provides complete data fetching capabilities with automatic state management, race condition
|
|
634
|
+
protection, and flexible configuration options. It includes built-in AbortController support inherited from `useExecutePromise`.
|
|
464
635
|
|
|
465
636
|
```typescript jsx
|
|
466
|
-
import {
|
|
637
|
+
import { useFetcher } from '@ahoo-wang/fetcher-react';
|
|
467
638
|
|
|
468
639
|
const MyComponent = () => {
|
|
469
|
-
const { loading,
|
|
640
|
+
const { loading, error, result, execute, abort } = useFetcher<string>({
|
|
470
641
|
onAbort: () => {
|
|
471
|
-
console.log('
|
|
642
|
+
console.log('Fetch operation was aborted');
|
|
472
643
|
}
|
|
473
644
|
});
|
|
474
645
|
|
|
475
|
-
const fetchData = async () => {
|
|
476
|
-
const response = await fetch('/api/data');
|
|
477
|
-
return response.text();
|
|
478
|
-
};
|
|
479
|
-
|
|
480
646
|
const handleFetch = () => {
|
|
481
|
-
execute(
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
const handleDirectPromise = () => {
|
|
485
|
-
const promise = fetch('/api/data').then(res => res.text());
|
|
486
|
-
execute(promise); // Using a direct promise
|
|
647
|
+
execute({ url: '/api/users', method: 'GET' });
|
|
487
648
|
};
|
|
488
649
|
|
|
489
650
|
const handleAbort = () => {
|
|
490
|
-
abort(); //
|
|
651
|
+
abort(); // Cancel the current fetch operation
|
|
491
652
|
};
|
|
492
|
-
|
|
493
|
-
if (loading) return <div>Loading...</div>;
|
|
494
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
495
|
-
return (
|
|
496
|
-
<div>
|
|
497
|
-
<button onClick={handleFetch}>Fetch with Supplier</button>
|
|
498
|
-
<button onClick={handleDirectPromise}>Fetch with Promise</button>
|
|
499
|
-
<button onClick={handleAbort} disabled={!loading}>Abort</button>
|
|
500
|
-
<button onClick={reset}>Reset</button>
|
|
501
|
-
{result && <p>{result}</p>}
|
|
502
|
-
</div>
|
|
503
|
-
);
|
|
504
|
-
};
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
#### Abort Controller Support
|
|
508
|
-
|
|
509
|
-
The hook automatically creates an AbortController for each operation and provides methods to manage cancellation:
|
|
510
|
-
|
|
511
|
-
- **Automatic Cleanup**: Operations are automatically aborted when the component unmounts
|
|
512
|
-
- **Manual Abort**: Use the `abort()` method to cancel ongoing operations
|
|
513
|
-
- **onAbort Callback**: Configure a callback that fires when an operation is aborted (manually or automatically)
|
|
514
|
-
- **AbortController Access**: The AbortController is passed to promise suppliers for advanced cancellation handling
|
|
515
|
-
|
|
516
|
-
### usePromiseState Hook
|
|
517
|
-
|
|
518
|
-
The `usePromiseState` hook provides state management for promise operations without execution logic. Supports both
|
|
519
|
-
static options and dynamic option suppliers.
|
|
520
|
-
|
|
521
|
-
```typescript jsx
|
|
522
|
-
import { usePromiseState, PromiseStatus } from '@ahoo-wang/fetcher-react';
|
|
523
|
-
|
|
524
|
-
const MyComponent = () => {
|
|
525
|
-
const { status, loading, result, error, setSuccess, setError, setIdle } = usePromiseState<string>();
|
|
526
|
-
|
|
527
|
-
const handleSuccess = () => setSuccess('Data loaded');
|
|
528
|
-
const handleError = () => setError(new Error('Failed to load'));
|
|
529
|
-
|
|
530
|
-
return (
|
|
531
|
-
<div>
|
|
532
|
-
<button onClick={handleSuccess}>Set Success</button>
|
|
533
|
-
<button onClick={handleError}>Set Error</button>
|
|
534
|
-
<button onClick={setIdle}>Reset</button>
|
|
535
|
-
<p>Status: {status}</p>
|
|
536
|
-
{loading && <p>Loading...</p>}
|
|
537
|
-
{result && <p>Result: {result}</p>}
|
|
538
|
-
{error && <p>Error: {error.message}</p>}
|
|
539
|
-
</div>
|
|
540
|
-
);
|
|
541
|
-
};
|
|
542
653
|
```
|
|
543
654
|
|
|
544
|
-
####
|
|
655
|
+
#### Auto Execute Example
|
|
545
656
|
|
|
546
657
|
```typescript jsx
|
|
547
|
-
import {
|
|
658
|
+
import { useListQuery } from '@ahoo-wang/fetcher-react';
|
|
548
659
|
|
|
549
660
|
const MyComponent = () => {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
await saveToAnalytics(result);
|
|
555
|
-
console.log('Success:', result);
|
|
556
|
-
},
|
|
557
|
-
onError: async (error) => {
|
|
558
|
-
await logErrorToServer(error);
|
|
559
|
-
console.error('Error:', error);
|
|
560
|
-
},
|
|
661
|
+
const { result, loading, error, execute, setCondition } = useListQuery({
|
|
662
|
+
initialQuery: { condition: {}, projection: {}, sort: [], limit: 10 },
|
|
663
|
+
list: async (listQuery) => fetchListData(listQuery),
|
|
664
|
+
autoExecute: true, // Automatically execute on component mount
|
|
561
665
|
});
|
|
562
666
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
return (
|
|
566
|
-
<div>
|
|
567
|
-
<button onClick={() => setSuccess('Dynamic success!')}>Set Success</button>
|
|
568
|
-
<button onClick={() => setError(new Error('Dynamic error!'))}>Set Error</button>
|
|
569
|
-
</div>
|
|
570
|
-
);
|
|
571
|
-
};
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### Utility Hooks
|
|
575
|
-
|
|
576
|
-
#### useRequestId Hook
|
|
577
|
-
|
|
578
|
-
The `useRequestId` hook provides request ID management for preventing race conditions in async operations.
|
|
579
|
-
|
|
580
|
-
```typescript jsx
|
|
581
|
-
import { useRequestId } from '@ahoo-wang/fetcher-react';
|
|
582
|
-
|
|
583
|
-
const MyComponent = () => {
|
|
584
|
-
const { generate, isLatest, invalidate } = useRequestId();
|
|
585
|
-
|
|
586
|
-
const handleFetch = async () => {
|
|
587
|
-
const requestId = generate();
|
|
588
|
-
|
|
589
|
-
try {
|
|
590
|
-
const result = await fetchData();
|
|
667
|
+
// The query will execute automatically when the component mounts
|
|
668
|
+
// You can still manually trigger it with execute() or update conditions
|
|
591
669
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
} catch (error) {
|
|
596
|
-
if (isLatest(requestId)) {
|
|
597
|
-
setError(error);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
};
|
|
670
|
+
if (loading) return <div>Loading...</div>;
|
|
671
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
601
672
|
|
|
602
673
|
return (
|
|
603
674
|
<div>
|
|
604
|
-
<
|
|
605
|
-
|
|
675
|
+
<ul>
|
|
676
|
+
{result?.map((item, index) => (
|
|
677
|
+
<li key={index}>{item.name}</li>
|
|
678
|
+
))}
|
|
679
|
+
</ul>
|
|
606
680
|
</div>
|
|
607
681
|
);
|
|
608
682
|
};
|
|
609
683
|
```
|
|
610
684
|
|
|
611
|
-
|
|
685
|
+
#### useFetcherQuery Hook
|
|
612
686
|
|
|
613
|
-
The `
|
|
614
|
-
callbacks.
|
|
687
|
+
The `useFetcherQuery` hook provides a foundation for building specialized query hooks that integrate with the Fetcher library.
|
|
615
688
|
|
|
616
689
|
```typescript jsx
|
|
617
|
-
import {
|
|
690
|
+
import { useFetcherQuery } from '@ahoo-wang/fetcher-react';
|
|
618
691
|
|
|
619
692
|
const MyComponent = () => {
|
|
620
|
-
const
|
|
621
|
-
|
|
693
|
+
const { data, loading, error, execute } = useFetcherQuery({
|
|
694
|
+
url: '/api/data',
|
|
695
|
+
initialQuery: { /* query parameters */ },
|
|
696
|
+
execute: async (query) => {
|
|
697
|
+
// Custom execution logic
|
|
698
|
+
return fetchData(query);
|
|
699
|
+
},
|
|
700
|
+
autoExecute: true,
|
|
701
|
+
});
|
|
622
702
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
console.log('Latest count:', latestCount.current); // Always the latest
|
|
626
|
-
};
|
|
703
|
+
if (loading) return <div>Loading...</div>;
|
|
704
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
627
705
|
|
|
628
706
|
return (
|
|
629
707
|
<div>
|
|
630
|
-
<
|
|
631
|
-
<button onClick={() => setCount(c => c + 1)}>Increment</button>
|
|
632
|
-
<button onClick={handleAsync}>Async Log</button>
|
|
708
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
633
709
|
</div>
|
|
634
710
|
);
|
|
635
711
|
};
|
|
636
712
|
```
|
|
637
713
|
|
|
638
|
-
###
|
|
714
|
+
### Debounced Fetcher Hooks
|
|
639
715
|
|
|
640
|
-
|
|
716
|
+
#### useDebouncedFetcher
|
|
717
|
+
|
|
718
|
+
Specialized hook combining HTTP fetching with debouncing, built on top of the core fetcher library.
|
|
641
719
|
|
|
642
720
|
```typescript jsx
|
|
643
|
-
import {
|
|
721
|
+
import { useDebouncedFetcher } from '@ahoo-wang/fetcher-react';
|
|
644
722
|
|
|
645
|
-
const
|
|
646
|
-
const
|
|
723
|
+
const SearchInput = () => {
|
|
724
|
+
const [query, setQuery] = useState('');
|
|
725
|
+
const { loading, result, error, run } = useDebouncedFetcher({
|
|
726
|
+
debounce: { delay: 300 },
|
|
727
|
+
onSuccess: (data) => {
|
|
728
|
+
setSearchResults(data.results);
|
|
729
|
+
}
|
|
730
|
+
});
|
|
647
731
|
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
732
|
+
const handleChange = (value: string) => {
|
|
733
|
+
setQuery(value);
|
|
734
|
+
if (value.trim()) {
|
|
735
|
+
run({
|
|
736
|
+
url: '/api/search',
|
|
737
|
+
method: 'GET',
|
|
738
|
+
params: { q: value }
|
|
739
|
+
});
|
|
740
|
+
}
|
|
651
741
|
};
|
|
652
742
|
|
|
653
743
|
return (
|
|
654
744
|
<div>
|
|
655
|
-
<
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
745
|
+
<input
|
|
746
|
+
value={query}
|
|
747
|
+
onChange={(e) => handleChange(e.target.value)}
|
|
748
|
+
placeholder="Search..."
|
|
749
|
+
/>
|
|
750
|
+
{loading && <div>Searching...</div>}
|
|
751
|
+
{error && <div>Error: {error.message}</div>}
|
|
752
|
+
{result && <SearchResults data={result} />}
|
|
659
753
|
</div>
|
|
660
754
|
);
|
|
661
755
|
};
|
|
662
756
|
```
|
|
663
757
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
- **Dynamic Registration**: Register refs with string, number, or symbol keys
|
|
667
|
-
- **Map-like API**: Full Map interface with get, set, has, delete, etc.
|
|
668
|
-
- **Automatic Cleanup**: Refs are cleared when component unmounts
|
|
669
|
-
- **Type Safety**: Full TypeScript support for ref types
|
|
758
|
+
**Debouncing Strategies:**
|
|
670
759
|
|
|
671
|
-
|
|
760
|
+
- **Leading Edge**: Execute immediately on first call, then debounce subsequent calls
|
|
761
|
+
- **Trailing Edge**: Execute after delay on last call (default behavior)
|
|
762
|
+
- **Leading + Trailing**: Execute immediately, then again after delay if called again
|
|
672
763
|
|
|
673
|
-
####
|
|
764
|
+
#### useDebouncedFetcherQuery
|
|
674
765
|
|
|
675
|
-
|
|
766
|
+
Combines query-based HTTP fetching with debouncing, perfect for search inputs and dynamic query scenarios where you want to debounce API calls based on query parameters.
|
|
676
767
|
|
|
677
768
|
```typescript jsx
|
|
678
|
-
import {
|
|
679
|
-
import { eventBus } from './eventBus';
|
|
769
|
+
import { useDebouncedFetcherQuery } from '@ahoo-wang/fetcher-react';
|
|
680
770
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
771
|
+
interface SearchQuery {
|
|
772
|
+
keyword: string;
|
|
773
|
+
limit: number;
|
|
774
|
+
filters?: { category?: string };
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
interface SearchResult {
|
|
778
|
+
items: Array<{ id: string; title: string }>;
|
|
779
|
+
total: number;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
const SearchComponent = () => {
|
|
783
|
+
const {
|
|
784
|
+
loading,
|
|
785
|
+
result,
|
|
786
|
+
error,
|
|
787
|
+
run,
|
|
788
|
+
cancel,
|
|
789
|
+
isPending,
|
|
790
|
+
setQuery,
|
|
791
|
+
getQuery,
|
|
792
|
+
} = useDebouncedFetcherQuery<SearchQuery, SearchResult>({
|
|
793
|
+
url: '/api/search',
|
|
794
|
+
initialQuery: { keyword: '', limit: 10 },
|
|
795
|
+
debounce: { delay: 300 }, // Debounce for 300ms
|
|
796
|
+
autoExecute: false, // Don't execute on mount
|
|
690
797
|
});
|
|
691
798
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const handleToggleSubscription = () => {
|
|
695
|
-
if (someCondition) {
|
|
696
|
-
subscribe();
|
|
697
|
-
} else {
|
|
698
|
-
unsubscribe();
|
|
699
|
-
}
|
|
799
|
+
const handleSearch = (keyword: string) => {
|
|
800
|
+
setQuery({ keyword, limit: 10 }); // This will trigger debounced execution if autoExecute was true
|
|
700
801
|
};
|
|
701
802
|
|
|
702
|
-
|
|
703
|
-
|
|
803
|
+
const handleManualSearch = () => {
|
|
804
|
+
run(); // Manual debounced execution with current query
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const handleCancel = () => {
|
|
808
|
+
cancel(); // Cancel any pending debounced execution
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
if (loading) return <div>Searching...</div>;
|
|
812
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
813
|
+
|
|
814
|
+
return (
|
|
815
|
+
<div>
|
|
816
|
+
<input
|
|
817
|
+
type="text"
|
|
818
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
819
|
+
placeholder="Search..."
|
|
820
|
+
/>
|
|
821
|
+
<button onClick={handleManualSearch} disabled={isPending()}>
|
|
822
|
+
{isPending() ? 'Searching...' : 'Search'}
|
|
823
|
+
</button>
|
|
824
|
+
<button onClick={handleCancel}>Cancel</button>
|
|
825
|
+
{result && (
|
|
826
|
+
<div>
|
|
827
|
+
Found {result.total} items:
|
|
828
|
+
{result.items.map(item => (
|
|
829
|
+
<div key={item.id}>{item.title}</div>
|
|
830
|
+
))}
|
|
831
|
+
</div>
|
|
832
|
+
)}
|
|
833
|
+
</div>
|
|
834
|
+
);
|
|
835
|
+
};
|
|
704
836
|
```
|
|
705
837
|
|
|
706
|
-
Key
|
|
838
|
+
**Key Features:**
|
|
707
839
|
|
|
708
|
-
- **
|
|
709
|
-
- **
|
|
710
|
-
- **
|
|
711
|
-
- **
|
|
712
|
-
- **
|
|
840
|
+
- **Query State Management**: Automatic query parameter handling with `setQuery` and `getQuery`
|
|
841
|
+
- **Debounced Execution**: Prevents excessive API calls during rapid user input
|
|
842
|
+
- **Auto-Execution**: Optional automatic execution when query parameters change
|
|
843
|
+
- **Manual Control**: `run()` for manual execution, `cancel()` for cancellation
|
|
844
|
+
- **Pending State**: `isPending()` to check if a debounced call is queued
|
|
713
845
|
|
|
714
846
|
### Storage Hooks
|
|
715
847
|
|
|
@@ -1040,22 +1172,188 @@ const prefsStorage = new KeyStorage<UserPreferences>({
|
|
|
1040
1172
|
key: 'user-prefs',
|
|
1041
1173
|
});
|
|
1042
1174
|
|
|
1043
|
-
// TypeScript will catch invalid updates
|
|
1044
|
-
const [prefs, updatePrefs] = useImmerKeyStorage(prefsStorage);
|
|
1175
|
+
// TypeScript will catch invalid updates
|
|
1176
|
+
const [prefs, updatePrefs] = useImmerKeyStorage(prefsStorage);
|
|
1177
|
+
|
|
1178
|
+
// This will cause a TypeScript error:
|
|
1179
|
+
// updatePrefs(draft => { draft.theme = 'invalid'; });
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
### Event Hooks
|
|
1183
|
+
|
|
1184
|
+
#### useEventSubscription Hook
|
|
1185
|
+
|
|
1186
|
+
The `useEventSubscription` hook provides a React interface for subscribing to typed event buses. It automatically manages subscription lifecycle while offering manual control functions for additional flexibility.
|
|
1187
|
+
|
|
1188
|
+
```typescript jsx
|
|
1189
|
+
import { useEventSubscription } from '@ahoo-wang/fetcher-react';
|
|
1190
|
+
import { eventBus } from './eventBus';
|
|
1191
|
+
|
|
1192
|
+
function MyComponent() {
|
|
1193
|
+
const { subscribe, unsubscribe } = useEventSubscription({
|
|
1194
|
+
bus: eventBus,
|
|
1195
|
+
handler: {
|
|
1196
|
+
name: 'myEvent',
|
|
1197
|
+
handle: (event) => {
|
|
1198
|
+
console.log('Received event:', event);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// The hook automatically subscribes on mount and unsubscribes on unmount
|
|
1204
|
+
// You can also manually control subscription if needed
|
|
1205
|
+
const handleToggleSubscription = () => {
|
|
1206
|
+
if (someCondition) {
|
|
1207
|
+
subscribe();
|
|
1208
|
+
} else {
|
|
1209
|
+
unsubscribe();
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
return <div>My Component</div>;
|
|
1214
|
+
}
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
Key features:
|
|
1218
|
+
|
|
1219
|
+
- **Automatic Lifecycle Management**: Automatically subscribes on component mount and unsubscribes on unmount
|
|
1220
|
+
- **Manual Control**: Provides `subscribe` and `unsubscribe` functions for additional control
|
|
1221
|
+
- **Type Safety**: Full TypeScript support with generic event types
|
|
1222
|
+
- **Error Handling**: Logs warnings for failed subscription attempts
|
|
1223
|
+
- **Event Bus Integration**: Works seamlessly with `@ahoo-wang/fetcher-eventbus` TypedEventBus instances
|
|
1224
|
+
|
|
1225
|
+
### CoSec Security Hooks
|
|
1226
|
+
|
|
1227
|
+
🛡️ **Enterprise Security Integration** - Powerful React hooks for managing authentication state with CoSec tokens, providing seamless integration with enterprise security systems and automatic token lifecycle management.
|
|
1228
|
+
|
|
1229
|
+
#### useSecurity Hook
|
|
1230
|
+
|
|
1231
|
+
The `useSecurity` hook provides reactive access to authentication state and operations using CoSec tokens. It integrates with TokenStorage to persist tokens and updates state reactively when tokens change.
|
|
1232
|
+
|
|
1233
|
+
```typescript jsx
|
|
1234
|
+
import { useSecurity } from '@ahoo-wang/fetcher-react';
|
|
1235
|
+
import { tokenStorage } from './tokenStorage';
|
|
1236
|
+
import { useNavigate } from 'react-router-dom';
|
|
1237
|
+
|
|
1238
|
+
function App() {
|
|
1239
|
+
const navigate = useNavigate();
|
|
1240
|
+
|
|
1241
|
+
const { currentUser, authenticated, signIn, signOut } = useSecurity(tokenStorage, {
|
|
1242
|
+
onSignIn: () => {
|
|
1243
|
+
// Redirect to dashboard after successful login
|
|
1244
|
+
navigate('/dashboard');
|
|
1245
|
+
},
|
|
1246
|
+
onSignOut: () => {
|
|
1247
|
+
// Redirect to login page after logout
|
|
1248
|
+
navigate('/login');
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
const handleSignIn = async () => {
|
|
1253
|
+
// Direct token
|
|
1254
|
+
await signIn(compositeToken);
|
|
1255
|
+
|
|
1256
|
+
// Or async function
|
|
1257
|
+
await signIn(async () => {
|
|
1258
|
+
const response = await fetch('/api/auth/login', {
|
|
1259
|
+
method: 'POST',
|
|
1260
|
+
body: JSON.stringify({ username, password })
|
|
1261
|
+
});
|
|
1262
|
+
return response.json();
|
|
1263
|
+
});
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
if (!authenticated) {
|
|
1267
|
+
return <button onClick={handleSignIn}>Sign In</button>;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
return (
|
|
1271
|
+
<div>
|
|
1272
|
+
<p>Welcome, {currentUser.sub}!</p>
|
|
1273
|
+
<button onClick={signOut}>Sign Out</button>
|
|
1274
|
+
</div>
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
**Key Features:**
|
|
1280
|
+
|
|
1281
|
+
- **Reactive Authentication State**: Automatically updates when tokens change
|
|
1282
|
+
- **Flexible Sign-in Methods**: Supports both direct tokens and async token providers
|
|
1283
|
+
- **Lifecycle Callbacks**: Configurable callbacks for sign-in and sign-out events
|
|
1284
|
+
- **Type Safety**: Full TypeScript support with CoSec JWT payload types
|
|
1285
|
+
- **Token Persistence**: Integrates with TokenStorage for cross-session persistence
|
|
1286
|
+
|
|
1287
|
+
#### SecurityProvider
|
|
1288
|
+
|
|
1289
|
+
The `SecurityProvider` component wraps your application to provide authentication context through React context. It internally uses the `useSecurity` hook and makes authentication state available to all child components via the `useSecurityContext` hook.
|
|
1290
|
+
|
|
1291
|
+
```tsx
|
|
1292
|
+
import { SecurityProvider } from '@ahoo-wang/fetcher-react';
|
|
1293
|
+
import { tokenStorage } from './tokenStorage';
|
|
1294
|
+
import { useNavigate } from 'react-router-dom';
|
|
1295
|
+
|
|
1296
|
+
function App() {
|
|
1297
|
+
const navigate = useNavigate();
|
|
1298
|
+
|
|
1299
|
+
return (
|
|
1300
|
+
<SecurityProvider
|
|
1301
|
+
tokenStorage={tokenStorage}
|
|
1302
|
+
onSignIn={() => navigate('/dashboard')}
|
|
1303
|
+
onSignOut={() => navigate('/login')}
|
|
1304
|
+
>
|
|
1305
|
+
<MyApp />
|
|
1306
|
+
</SecurityProvider>
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
**Configuration Options:**
|
|
1312
|
+
|
|
1313
|
+
- `tokenStorage`: TokenStorage instance for managing authentication tokens
|
|
1314
|
+
- `onSignIn`: Callback function invoked when sign in is successful
|
|
1315
|
+
- `onSignOut`: Callback function invoked when sign out occurs
|
|
1316
|
+
- `children`: Child components that will have access to security context
|
|
1317
|
+
|
|
1318
|
+
#### useSecurityContext Hook
|
|
1319
|
+
|
|
1320
|
+
The `useSecurityContext` hook provides access to authentication state and methods within components wrapped by `SecurityProvider`. It offers the same interface as `useSecurity` but through React context.
|
|
1321
|
+
|
|
1322
|
+
```tsx
|
|
1323
|
+
import { useSecurityContext } from '@ahoo-wang/fetcher-react';
|
|
1045
1324
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1325
|
+
function UserProfile() {
|
|
1326
|
+
const { currentUser, authenticated, signOut } = useSecurityContext();
|
|
1327
|
+
|
|
1328
|
+
if (!authenticated) {
|
|
1329
|
+
return <div>Please sign in</div>;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
return (
|
|
1333
|
+
<div>
|
|
1334
|
+
<p>Welcome, {currentUser.sub}!</p>
|
|
1335
|
+
<button onClick={signOut}>Sign Out</button>
|
|
1336
|
+
</div>
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1048
1339
|
```
|
|
1049
1340
|
|
|
1050
|
-
|
|
1341
|
+
**Context Benefits:**
|
|
1342
|
+
|
|
1343
|
+
- **Prop Drilling Elimination**: Access authentication state without passing props
|
|
1344
|
+
- **Component Isolation**: Components can access auth state regardless of component tree depth
|
|
1345
|
+
- **Centralized State**: Single source of truth for authentication across the application
|
|
1346
|
+
- **Automatic Re-rendering**: Components automatically re-render when authentication state changes
|
|
1347
|
+
|
|
1348
|
+
### Wow Query Hooks
|
|
1051
1349
|
|
|
1052
1350
|
The Wow Query Hooks provide advanced data querying capabilities with built-in state management for conditions,
|
|
1053
1351
|
projections, sorting, pagination, and limits. These hooks are designed to work with the `@ahoo-wang/fetcher-wow` package
|
|
1054
1352
|
for complex query operations.
|
|
1055
1353
|
|
|
1056
|
-
|
|
1354
|
+
#### Basic Query Hooks
|
|
1057
1355
|
|
|
1058
|
-
|
|
1356
|
+
##### useListQuery Hook
|
|
1059
1357
|
|
|
1060
1358
|
The `useListQuery` hook manages list queries with state management for conditions, projections, sorting, and limits.
|
|
1061
1359
|
|
|
@@ -1092,7 +1390,7 @@ const MyComponent = () => {
|
|
|
1092
1390
|
};
|
|
1093
1391
|
```
|
|
1094
1392
|
|
|
1095
|
-
|
|
1393
|
+
##### Auto Execute Example
|
|
1096
1394
|
|
|
1097
1395
|
```typescript jsx
|
|
1098
1396
|
import { useListQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1122,7 +1420,7 @@ const MyComponent = () => {
|
|
|
1122
1420
|
};
|
|
1123
1421
|
```
|
|
1124
1422
|
|
|
1125
|
-
|
|
1423
|
+
##### usePagedQuery Hook
|
|
1126
1424
|
|
|
1127
1425
|
The `usePagedQuery` hook manages paged queries with state management for conditions, projections, pagination, and
|
|
1128
1426
|
sorting.
|
|
@@ -1170,7 +1468,7 @@ const MyComponent = () => {
|
|
|
1170
1468
|
};
|
|
1171
1469
|
```
|
|
1172
1470
|
|
|
1173
|
-
|
|
1471
|
+
###### Auto Execute Example
|
|
1174
1472
|
|
|
1175
1473
|
```typescript jsx
|
|
1176
1474
|
import { usePagedQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1210,7 +1508,7 @@ const MyComponent = () => {
|
|
|
1210
1508
|
};
|
|
1211
1509
|
```
|
|
1212
1510
|
|
|
1213
|
-
|
|
1511
|
+
##### useSingleQuery Hook
|
|
1214
1512
|
|
|
1215
1513
|
The `useSingleQuery` hook manages single item queries with state management for conditions, projections, and sorting.
|
|
1216
1514
|
|
|
@@ -1243,7 +1541,7 @@ const MyComponent = () => {
|
|
|
1243
1541
|
};
|
|
1244
1542
|
```
|
|
1245
1543
|
|
|
1246
|
-
|
|
1544
|
+
###### Auto Execute Example
|
|
1247
1545
|
|
|
1248
1546
|
```typescript jsx
|
|
1249
1547
|
import { useSingleQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1268,7 +1566,7 @@ const MyComponent = () => {
|
|
|
1268
1566
|
};
|
|
1269
1567
|
```
|
|
1270
1568
|
|
|
1271
|
-
|
|
1569
|
+
##### useCountQuery Hook
|
|
1272
1570
|
|
|
1273
1571
|
The `useCountQuery` hook manages count queries with state management for conditions.
|
|
1274
1572
|
|
|
@@ -1301,7 +1599,7 @@ const MyComponent = () => {
|
|
|
1301
1599
|
};
|
|
1302
1600
|
```
|
|
1303
1601
|
|
|
1304
|
-
|
|
1602
|
+
###### Auto Execute Example
|
|
1305
1603
|
|
|
1306
1604
|
```typescript jsx
|
|
1307
1605
|
import { useCountQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1326,9 +1624,99 @@ const MyComponent = () => {
|
|
|
1326
1624
|
};
|
|
1327
1625
|
```
|
|
1328
1626
|
|
|
1329
|
-
|
|
1627
|
+
##### useListStreamQuery Hook
|
|
1628
|
+
|
|
1629
|
+
The `useListStreamQuery` hook manages list stream queries that return a readable stream of server-sent events.
|
|
1630
|
+
|
|
1631
|
+
```typescript jsx
|
|
1632
|
+
import { useListStreamQuery } from '@ahoo-wang/fetcher-react';
|
|
1633
|
+
|
|
1634
|
+
const MyComponent = () => {
|
|
1635
|
+
const { result, loading, error, execute, setCondition } = useListStreamQuery({
|
|
1636
|
+
initialQuery: { condition: {}, projection: {}, sort: [], limit: 100 },
|
|
1637
|
+
execute: async (listQuery) => {
|
|
1638
|
+
// Your stream fetching logic here
|
|
1639
|
+
return fetchListStream(listQuery);
|
|
1640
|
+
},
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
useEffect(() => {
|
|
1644
|
+
if (result) {
|
|
1645
|
+
const reader = result.getReader();
|
|
1646
|
+
const readStream = async () => {
|
|
1647
|
+
try {
|
|
1648
|
+
while (true) {
|
|
1649
|
+
const { done, value } = await reader.read();
|
|
1650
|
+
if (done) break;
|
|
1651
|
+
console.log('Received:', value);
|
|
1652
|
+
// Process the stream event
|
|
1653
|
+
}
|
|
1654
|
+
} catch (error) {
|
|
1655
|
+
console.error('Stream error:', error);
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
readStream();
|
|
1659
|
+
}
|
|
1660
|
+
}, [result]);
|
|
1661
|
+
|
|
1662
|
+
if (loading) return <div>Loading...</div>;
|
|
1663
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
1664
|
+
|
|
1665
|
+
return (
|
|
1666
|
+
<div>
|
|
1667
|
+
<button onClick={execute}>Start Stream</button>
|
|
1668
|
+
</div>
|
|
1669
|
+
);
|
|
1670
|
+
};
|
|
1671
|
+
```
|
|
1672
|
+
|
|
1673
|
+
###### Auto Execute Example
|
|
1674
|
+
|
|
1675
|
+
```typescript jsx
|
|
1676
|
+
import { useListStreamQuery } from '@ahoo-wang/fetcher-react';
|
|
1677
|
+
|
|
1678
|
+
const MyComponent = () => {
|
|
1679
|
+
const { result, loading, error, execute, setCondition } = useListStreamQuery({
|
|
1680
|
+
initialQuery: { condition: {}, projection: {}, sort: [], limit: 100 },
|
|
1681
|
+
execute: async (listQuery) => fetchListStream(listQuery),
|
|
1682
|
+
autoExecute: true, // Automatically execute on component mount
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1685
|
+
useEffect(() => {
|
|
1686
|
+
if (result) {
|
|
1687
|
+
const reader = result.getReader();
|
|
1688
|
+
const readStream = async () => {
|
|
1689
|
+
try {
|
|
1690
|
+
while (true) {
|
|
1691
|
+
const { done, value } = await reader.read();
|
|
1692
|
+
if (done) break;
|
|
1693
|
+
console.log('Received:', value);
|
|
1694
|
+
// Process the stream event
|
|
1695
|
+
}
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
console.error('Stream error:', error);
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
readStream();
|
|
1701
|
+
}
|
|
1702
|
+
}, [result]);
|
|
1703
|
+
|
|
1704
|
+
// The query will execute automatically when the component mounts
|
|
1705
|
+
|
|
1706
|
+
if (loading) return <div>Loading...</div>;
|
|
1707
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
1708
|
+
|
|
1709
|
+
return (
|
|
1710
|
+
<div>
|
|
1711
|
+
{/* Stream is already started automatically */}
|
|
1712
|
+
</div>
|
|
1713
|
+
);
|
|
1714
|
+
};
|
|
1715
|
+
```
|
|
1716
|
+
|
|
1717
|
+
#### Fetcher Query Hooks
|
|
1330
1718
|
|
|
1331
|
-
|
|
1719
|
+
##### useFetcherCountQuery Hook
|
|
1332
1720
|
|
|
1333
1721
|
The `useFetcherCountQuery` hook is a specialized React hook for performing count queries using the Fetcher library. It is designed for scenarios where you need to retrieve the count of records that match a specific condition, returning a number representing the count.
|
|
1334
1722
|
|
|
@@ -1352,7 +1740,7 @@ function UserCountComponent() {
|
|
|
1352
1740
|
}
|
|
1353
1741
|
```
|
|
1354
1742
|
|
|
1355
|
-
|
|
1743
|
+
###### Auto Execute Example
|
|
1356
1744
|
|
|
1357
1745
|
```typescript jsx
|
|
1358
1746
|
import { useFetcherCountQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1373,7 +1761,7 @@ const MyComponent = () => {
|
|
|
1373
1761
|
};
|
|
1374
1762
|
```
|
|
1375
1763
|
|
|
1376
|
-
|
|
1764
|
+
##### useFetcherPagedQuery Hook
|
|
1377
1765
|
|
|
1378
1766
|
The `useFetcherPagedQuery` hook is a specialized React hook for performing paged queries using the Fetcher library. It is designed for scenarios where you need to retrieve paginated data that matches a query condition, returning a PagedList containing the items for the current page along with pagination metadata.
|
|
1379
1767
|
|
|
@@ -1442,7 +1830,7 @@ function UserListComponent() {
|
|
|
1442
1830
|
}
|
|
1443
1831
|
```
|
|
1444
1832
|
|
|
1445
|
-
|
|
1833
|
+
###### Auto Execute Example
|
|
1446
1834
|
|
|
1447
1835
|
```typescript jsx
|
|
1448
1836
|
import { useFetcherPagedQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1478,7 +1866,7 @@ const MyComponent = () => {
|
|
|
1478
1866
|
};
|
|
1479
1867
|
```
|
|
1480
1868
|
|
|
1481
|
-
|
|
1869
|
+
##### useFetcherListQuery Hook
|
|
1482
1870
|
|
|
1483
1871
|
The `useFetcherListQuery` hook is a specialized React hook for performing list queries using the Fetcher library. It is designed for fetching lists of items with support for filtering, sorting, and pagination through the ListQuery type, returning an array of results.
|
|
1484
1872
|
|
|
@@ -1539,7 +1927,7 @@ function UserListComponent() {
|
|
|
1539
1927
|
}
|
|
1540
1928
|
```
|
|
1541
1929
|
|
|
1542
|
-
|
|
1930
|
+
###### Auto Execute Example
|
|
1543
1931
|
|
|
1544
1932
|
```typescript jsx
|
|
1545
1933
|
import { useFetcherListQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1574,7 +1962,7 @@ const MyComponent = () => {
|
|
|
1574
1962
|
};
|
|
1575
1963
|
```
|
|
1576
1964
|
|
|
1577
|
-
|
|
1965
|
+
##### useFetcherListStreamQuery Hook
|
|
1578
1966
|
|
|
1579
1967
|
The `useFetcherListStreamQuery` hook is a specialized React hook for performing list stream queries using the Fetcher library with server-sent events. It is designed for scenarios where you need to retrieve a stream of data that matches a list query condition, returning a ReadableStream of JSON server-sent events for real-time data streaming.
|
|
1580
1968
|
|
|
@@ -1637,7 +2025,7 @@ function UserStreamComponent() {
|
|
|
1637
2025
|
}
|
|
1638
2026
|
```
|
|
1639
2027
|
|
|
1640
|
-
|
|
2028
|
+
###### Auto Execute Example
|
|
1641
2029
|
|
|
1642
2030
|
```typescript jsx
|
|
1643
2031
|
import { useFetcherListStreamQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1693,7 +2081,7 @@ const MyComponent = () => {
|
|
|
1693
2081
|
};
|
|
1694
2082
|
```
|
|
1695
2083
|
|
|
1696
|
-
|
|
2084
|
+
##### useFetcherSingleQuery Hook
|
|
1697
2085
|
|
|
1698
2086
|
The `useFetcherSingleQuery` hook is a specialized React hook for performing single item queries using the Fetcher library. It is designed for fetching a single item with support for filtering and sorting through the SingleQuery type, returning a single result item.
|
|
1699
2087
|
|
|
@@ -1737,7 +2125,7 @@ function UserProfileComponent({ userId }: { userId: string }) {
|
|
|
1737
2125
|
}
|
|
1738
2126
|
```
|
|
1739
2127
|
|
|
1740
|
-
|
|
2128
|
+
###### Auto Execute Example
|
|
1741
2129
|
|
|
1742
2130
|
```typescript jsx
|
|
1743
2131
|
import { useFetcherSingleQuery } from '@ahoo-wang/fetcher-react';
|
|
@@ -1769,103 +2157,11 @@ const MyComponent = () => {
|
|
|
1769
2157
|
};
|
|
1770
2158
|
```
|
|
1771
2159
|
|
|
1772
|
-
### Stream Query Hooks
|
|
1773
|
-
|
|
1774
|
-
#### useListStreamQuery Hook
|
|
1775
|
-
|
|
1776
|
-
The `useListStreamQuery` hook manages list stream queries that return a readable stream of server-sent events.
|
|
1777
|
-
|
|
1778
|
-
```typescript jsx
|
|
1779
|
-
import { useListStreamQuery } from '@ahoo-wang/fetcher-react';
|
|
1780
|
-
|
|
1781
|
-
const MyComponent = () => {
|
|
1782
|
-
const { result, loading, error, execute, setCondition } = useListStreamQuery({
|
|
1783
|
-
initialQuery: { condition: {}, projection: {}, sort: [], limit: 100 },
|
|
1784
|
-
execute: async (listQuery) => {
|
|
1785
|
-
// Your stream fetching logic here
|
|
1786
|
-
return fetchListStream(listQuery);
|
|
1787
|
-
},
|
|
1788
|
-
});
|
|
1789
|
-
|
|
1790
|
-
useEffect(() => {
|
|
1791
|
-
if (result) {
|
|
1792
|
-
const reader = result.getReader();
|
|
1793
|
-
const readStream = async () => {
|
|
1794
|
-
try {
|
|
1795
|
-
while (true) {
|
|
1796
|
-
const { done, value } = await reader.read();
|
|
1797
|
-
if (done) break;
|
|
1798
|
-
console.log('Received:', value);
|
|
1799
|
-
// Process the stream event
|
|
1800
|
-
}
|
|
1801
|
-
} catch (error) {
|
|
1802
|
-
console.error('Stream error:', error);
|
|
1803
|
-
}
|
|
1804
|
-
};
|
|
1805
|
-
readStream();
|
|
1806
|
-
}
|
|
1807
|
-
}, [result]);
|
|
1808
|
-
|
|
1809
|
-
if (loading) return <div>Loading...</div>;
|
|
1810
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
1811
|
-
|
|
1812
|
-
return (
|
|
1813
|
-
<div>
|
|
1814
|
-
<button onClick={execute}>Start Stream</button>
|
|
1815
|
-
</div>
|
|
1816
|
-
);
|
|
1817
|
-
};
|
|
1818
|
-
```
|
|
1819
|
-
|
|
1820
|
-
#### Auto Execute Example
|
|
1821
|
-
|
|
1822
|
-
```typescript jsx
|
|
1823
|
-
import { useListStreamQuery } from '@ahoo-wang/fetcher-react';
|
|
1824
|
-
|
|
1825
|
-
const MyComponent = () => {
|
|
1826
|
-
const { result, loading, error, execute, setCondition } = useListStreamQuery({
|
|
1827
|
-
initialQuery: { condition: {}, projection: {}, sort: [], limit: 100 },
|
|
1828
|
-
execute: async (listQuery) => fetchListStream(listQuery),
|
|
1829
|
-
autoExecute: true, // Automatically execute on component mount
|
|
1830
|
-
});
|
|
1831
|
-
|
|
1832
|
-
useEffect(() => {
|
|
1833
|
-
if (result) {
|
|
1834
|
-
const reader = result.getReader();
|
|
1835
|
-
const readStream = async () => {
|
|
1836
|
-
try {
|
|
1837
|
-
while (true) {
|
|
1838
|
-
const { done, value } = await reader.read();
|
|
1839
|
-
if (done) break;
|
|
1840
|
-
console.log('Received:', value);
|
|
1841
|
-
// Process the stream event
|
|
1842
|
-
}
|
|
1843
|
-
} catch (error) {
|
|
1844
|
-
console.error('Stream error:', error);
|
|
1845
|
-
}
|
|
1846
|
-
};
|
|
1847
|
-
readStream();
|
|
1848
|
-
}
|
|
1849
|
-
}, [result]);
|
|
1850
|
-
|
|
1851
|
-
// The query will execute automatically when the component mounts
|
|
1852
|
-
|
|
1853
|
-
if (loading) return <div>Loading...</div>;
|
|
1854
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
1855
|
-
|
|
1856
|
-
return (
|
|
1857
|
-
<div>
|
|
1858
|
-
{/* Stream is already started automatically */}
|
|
1859
|
-
</div>
|
|
1860
|
-
);
|
|
1861
|
-
};
|
|
1862
|
-
```
|
|
1863
|
-
|
|
1864
2160
|
## Best Practices
|
|
1865
2161
|
|
|
1866
2162
|
### Performance Optimization
|
|
1867
2163
|
|
|
1868
|
-
- Use `autoExecute:
|
|
2164
|
+
- Use `autoExecute: false` when you need to control when queries execute
|
|
1869
2165
|
- Leverage `setQuery` for query updates when `autoExecute` is enabled to trigger automatic re-execution
|
|
1870
2166
|
- Memoize expensive computations in your `execute` functions
|
|
1871
2167
|
|
|
@@ -1943,7 +2239,7 @@ import { Component, ErrorInfo, ReactNode } from 'react';
|
|
|
1943
2239
|
class FetchErrorBoundary extends Component<
|
|
1944
2240
|
{ children: ReactNode; fallback?: ReactNode },
|
|
1945
2241
|
{ hasError: boolean; error?: Error }
|
|
1946
|
-
> {
|
|
2242
|
+
> {
|
|
1947
2243
|
constructor(props: { children: ReactNode; fallback?: ReactNode }) {
|
|
1948
2244
|
super(props);
|
|
1949
2245
|
this.state = { hasError: false };
|
|
@@ -2725,6 +3021,96 @@ An object implementing `UseRefsReturn<T>` with:
|
|
|
2725
3021
|
- `RefKey = string | number | symbol`
|
|
2726
3022
|
- `UseRefsReturn<T> extends Iterable<[RefKey, T]>`
|
|
2727
3023
|
|
|
3024
|
+
### useQuery
|
|
3025
|
+
|
|
3026
|
+
```typescript
|
|
3027
|
+
function useQuery<Q, R, E = FetcherError>(
|
|
3028
|
+
options: UseQueryOptions<Q, R, E>,
|
|
3029
|
+
): UseQueryReturn<Q, R, E>;
|
|
3030
|
+
```
|
|
3031
|
+
|
|
3032
|
+
A React hook for managing query-based asynchronous operations with automatic state management and execution control.
|
|
3033
|
+
|
|
3034
|
+
**Type Parameters:**
|
|
3035
|
+
|
|
3036
|
+
- `Q`: The type of the query parameters
|
|
3037
|
+
- `R`: The type of the result value
|
|
3038
|
+
- `E`: The type of the error value (defaults to FetcherError)
|
|
3039
|
+
|
|
3040
|
+
**Parameters:**
|
|
3041
|
+
|
|
3042
|
+
- `options`: Configuration options for the query
|
|
3043
|
+
- `initialQuery`: The initial query parameters
|
|
3044
|
+
- `execute`: Function to execute the query with given parameters and optional attributes
|
|
3045
|
+
- `autoExecute?`: Whether to automatically execute the query on mount and when query changes
|
|
3046
|
+
- All options from `UseExecutePromiseOptions`
|
|
3047
|
+
|
|
3048
|
+
**Returns:**
|
|
3049
|
+
|
|
3050
|
+
An object containing the query state and control functions:
|
|
3051
|
+
|
|
3052
|
+
- `loading`: Boolean indicating if the query is currently executing
|
|
3053
|
+
- `result`: The resolved value of the query
|
|
3054
|
+
- `error`: Any error that occurred during execution
|
|
3055
|
+
- `status`: Current execution status
|
|
3056
|
+
- `execute`: Function to execute the query with current parameters
|
|
3057
|
+
- `reset`: Function to reset the promise state
|
|
3058
|
+
- `abort`: Function to abort the current operation
|
|
3059
|
+
- `getQuery`: Function to retrieve the current query parameters
|
|
3060
|
+
- `setQuery`: Function to update the query parameters
|
|
3061
|
+
|
|
3062
|
+
### useQueryState
|
|
3063
|
+
|
|
3064
|
+
```typescript
|
|
3065
|
+
function useQueryState<Q>(
|
|
3066
|
+
options: UseQueryStateOptions<Q>,
|
|
3067
|
+
): UseQueryStateReturn<Q>;
|
|
3068
|
+
```
|
|
3069
|
+
|
|
3070
|
+
A React hook for managing query state with automatic execution capabilities.
|
|
3071
|
+
|
|
3072
|
+
**Type Parameters:**
|
|
3073
|
+
|
|
3074
|
+
- `Q`: The type of the query parameters
|
|
3075
|
+
|
|
3076
|
+
**Parameters:**
|
|
3077
|
+
|
|
3078
|
+
- `options`: Configuration options for the hook
|
|
3079
|
+
- `initialQuery`: The initial query parameters to be stored and managed
|
|
3080
|
+
- `autoExecute?`: Whether to automatically execute when the query changes or on component mount
|
|
3081
|
+
- `execute`: Function to execute with the current query parameters
|
|
3082
|
+
|
|
3083
|
+
**Returns:**
|
|
3084
|
+
|
|
3085
|
+
An object containing:
|
|
3086
|
+
|
|
3087
|
+
- `getQuery`: Function to retrieve the current query parameters
|
|
3088
|
+
- `setQuery`: Function to update the query parameters. Triggers execution if autoExecute is true
|
|
3089
|
+
|
|
3090
|
+
### useMounted
|
|
3091
|
+
|
|
3092
|
+
```typescript
|
|
3093
|
+
function useMounted(): () => boolean;
|
|
3094
|
+
```
|
|
3095
|
+
|
|
3096
|
+
A React hook that returns a function to check if the component is still mounted.
|
|
3097
|
+
|
|
3098
|
+
**Returns:**
|
|
3099
|
+
|
|
3100
|
+
A function that returns `true` if the component is still mounted, `false` otherwise.
|
|
3101
|
+
|
|
3102
|
+
### useForceUpdate
|
|
3103
|
+
|
|
3104
|
+
```typescript
|
|
3105
|
+
function useForceUpdate(): () => void;
|
|
3106
|
+
```
|
|
3107
|
+
|
|
3108
|
+
A React hook that returns a function to force a component to re-render.
|
|
3109
|
+
|
|
3110
|
+
**Returns:**
|
|
3111
|
+
|
|
3112
|
+
A function that forces the component to re-render when called.
|
|
3113
|
+
|
|
2728
3114
|
### useEventSubscription
|
|
2729
3115
|
|
|
2730
3116
|
```typescript
|
|
@@ -2886,7 +3272,7 @@ A React hook for managing list queries with state management for conditions, pro
|
|
|
2886
3272
|
**Parameters:**
|
|
2887
3273
|
|
|
2888
3274
|
- `options`: Configuration options including initialQuery and list function
|
|
2889
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3275
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
2890
3276
|
|
|
2891
3277
|
**Returns:**
|
|
2892
3278
|
|
|
@@ -2911,7 +3297,7 @@ A React hook for managing paged queries with state management for conditions, pr
|
|
|
2911
3297
|
**Parameters:**
|
|
2912
3298
|
|
|
2913
3299
|
- `options`: Configuration options including initialQuery and query function
|
|
2914
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3300
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
2915
3301
|
|
|
2916
3302
|
**Returns:**
|
|
2917
3303
|
|
|
@@ -2936,7 +3322,7 @@ A React hook for managing single queries with state management for conditions, p
|
|
|
2936
3322
|
**Parameters:**
|
|
2937
3323
|
|
|
2938
3324
|
- `options`: Configuration options including initialQuery and query function
|
|
2939
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3325
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
2940
3326
|
|
|
2941
3327
|
**Returns:**
|
|
2942
3328
|
|
|
@@ -2960,7 +3346,7 @@ A React hook for managing count queries with state management for conditions.
|
|
|
2960
3346
|
**Parameters:**
|
|
2961
3347
|
|
|
2962
3348
|
- `options`: Configuration options including initialQuery and execute function
|
|
2963
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3349
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
2964
3350
|
|
|
2965
3351
|
**Returns:**
|
|
2966
3352
|
|
|
@@ -2986,7 +3372,7 @@ A React hook for performing count queries using the Fetcher library. It wraps th
|
|
|
2986
3372
|
- `options`: Configuration options for the count query, including the condition, fetcher instance, and other query settings
|
|
2987
3373
|
- `url`: The URL to fetch the count from
|
|
2988
3374
|
- `initialQuery`: The initial condition for the count query
|
|
2989
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3375
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
2990
3376
|
|
|
2991
3377
|
**Returns:**
|
|
2992
3378
|
|
|
@@ -3017,7 +3403,7 @@ A React hook for performing paged queries using the Fetcher library. It wraps th
|
|
|
3017
3403
|
- `options`: Configuration options for the paged query, including the paged query parameters, fetcher instance, and other query settings
|
|
3018
3404
|
- `url`: The URL to fetch the paged data from
|
|
3019
3405
|
- `initialQuery`: The initial paged query configuration
|
|
3020
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3406
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
3021
3407
|
|
|
3022
3408
|
**Returns:**
|
|
3023
3409
|
|
|
@@ -3048,7 +3434,7 @@ A React hook for executing list queries using the fetcher library within the wow
|
|
|
3048
3434
|
- `options`: Configuration options for the list query, including the list query parameters, fetcher instance, and other query settings
|
|
3049
3435
|
- `url`: The URL to fetch the list data from
|
|
3050
3436
|
- `initialQuery`: The initial list query configuration
|
|
3051
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3437
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
3052
3438
|
|
|
3053
3439
|
**Returns:**
|
|
3054
3440
|
|
|
@@ -3079,7 +3465,7 @@ A React hook for performing list stream queries using the Fetcher library with s
|
|
|
3079
3465
|
- `options`: Configuration options for the list stream query, including the list query parameters, fetcher instance, and other query settings
|
|
3080
3466
|
- `url`: The URL to fetch the stream data from
|
|
3081
3467
|
- `initialQuery`: The initial list query configuration
|
|
3082
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3468
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
3083
3469
|
|
|
3084
3470
|
**Returns:**
|
|
3085
3471
|
|
|
@@ -3110,7 +3496,7 @@ A React hook for executing single item queries using the fetcher library within
|
|
|
3110
3496
|
- `options`: Configuration options for the single query, including the single query parameters, fetcher instance, and other query settings
|
|
3111
3497
|
- `url`: The URL to fetch the single item from
|
|
3112
3498
|
- `initialQuery`: The initial single query configuration
|
|
3113
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3499
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
3114
3500
|
|
|
3115
3501
|
**Returns:**
|
|
3116
3502
|
|
|
@@ -3140,7 +3526,7 @@ Returns a readable stream of JSON server-sent events.
|
|
|
3140
3526
|
**Parameters:**
|
|
3141
3527
|
|
|
3142
3528
|
- `options`: Configuration options including initialQuery and listStream function
|
|
3143
|
-
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to
|
|
3529
|
+
- `autoExecute`: Whether to automatically execute the query on component mount (defaults to true)
|
|
3144
3530
|
|
|
3145
3531
|
**Returns:**
|
|
3146
3532
|
|