@intrig/plugin-react 0.0.1 → 0.0.2-2
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/dist/index.cjs +4260 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4235 -0
- package/package.json +6 -3
- package/.swcrc +0 -29
- package/README.md +0 -7
- package/eslint.config.mjs +0 -19
- package/project.json +0 -29
- package/rollup.config.cjs +0 -54
- package/rollup.config.mjs +0 -33
- package/src/index.ts +0 -2
- package/src/lib/code-generator.ts +0 -79
- package/src/lib/get-endpoint-documentation.ts +0 -35
- package/src/lib/get-schema-documentation.ts +0 -11
- package/src/lib/internal-types.ts +0 -15
- package/src/lib/plugin-react.ts +0 -22
- package/src/lib/templates/context.template.ts +0 -74
- package/src/lib/templates/docs/__snapshots__/async-hook.spec.ts.snap +0 -889
- package/src/lib/templates/docs/__snapshots__/download-hook.spec.ts.snap +0 -1445
- package/src/lib/templates/docs/__snapshots__/react-hook.spec.ts.snap +0 -1371
- package/src/lib/templates/docs/__snapshots__/sse-hook.spec.ts.snap +0 -2008
- package/src/lib/templates/docs/async-hook.spec.ts +0 -92
- package/src/lib/templates/docs/async-hook.ts +0 -226
- package/src/lib/templates/docs/download-hook.spec.ts +0 -182
- package/src/lib/templates/docs/download-hook.ts +0 -170
- package/src/lib/templates/docs/react-hook.spec.ts +0 -97
- package/src/lib/templates/docs/react-hook.ts +0 -323
- package/src/lib/templates/docs/schema.ts +0 -105
- package/src/lib/templates/docs/sse-hook.spec.ts +0 -207
- package/src/lib/templates/docs/sse-hook.ts +0 -221
- package/src/lib/templates/extra.template.ts +0 -198
- package/src/lib/templates/index.template.ts +0 -14
- package/src/lib/templates/intrigMiddleware.template.ts +0 -21
- package/src/lib/templates/logger.template.ts +0 -67
- package/src/lib/templates/media-type-utils.template.ts +0 -191
- package/src/lib/templates/network-state.template.ts +0 -702
- package/src/lib/templates/packageJson.template.ts +0 -63
- package/src/lib/templates/provider/__tests__/provider-templates.spec.ts +0 -209
- package/src/lib/templates/provider/axios-config.template.ts +0 -49
- package/src/lib/templates/provider/hooks.template.ts +0 -240
- package/src/lib/templates/provider/interfaces.template.ts +0 -72
- package/src/lib/templates/provider/intrig-provider-stub.template.ts +0 -73
- package/src/lib/templates/provider/intrig-provider.template.ts +0 -185
- package/src/lib/templates/provider/main.template.ts +0 -48
- package/src/lib/templates/provider/reducer.template.ts +0 -50
- package/src/lib/templates/provider/status-trap.template.ts +0 -80
- package/src/lib/templates/provider.template.ts +0 -698
- package/src/lib/templates/source/controller/method/asyncFunctionHook.template.ts +0 -196
- package/src/lib/templates/source/controller/method/clientIndex.template.ts +0 -38
- package/src/lib/templates/source/controller/method/download.template.ts +0 -256
- package/src/lib/templates/source/controller/method/params.template.ts +0 -31
- package/src/lib/templates/source/controller/method/requestHook.template.ts +0 -220
- package/src/lib/templates/source/type/typeTemplate.ts +0 -257
- package/src/lib/templates/swcrc.template.ts +0 -25
- package/src/lib/templates/tsconfig.template.ts +0 -37
- package/src/lib/templates/type-utils.template.ts +0 -28
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -20
|
@@ -1,1371 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`reactHookDocs > generates markdown that matches snapshot for a simple REST descriptor (no body, no path params) 1`] = `
|
|
4
|
-
"# Intrig React Hooks — Quick Guide
|
|
5
|
-
|
|
6
|
-
## Copy-paste starter (fast lane)
|
|
7
|
-
|
|
8
|
-
### 1) Hook import
|
|
9
|
-
|
|
10
|
-
\`\`\`ts
|
|
11
|
-
import { useGetUser } from "@intrig/react/users/client";
|
|
12
|
-
\`\`\`
|
|
13
|
-
|
|
14
|
-
### 2) Utility guards
|
|
15
|
-
|
|
16
|
-
\`\`\`ts
|
|
17
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
18
|
-
\`\`\`
|
|
19
|
-
|
|
20
|
-
### 3) Hook instance (auto-clear on unmount)
|
|
21
|
-
|
|
22
|
-
\`\`\`ts
|
|
23
|
-
const [getUserResp, getUser] = useGetUser({ clearOnUnmount: true });
|
|
24
|
-
\`\`\`
|
|
25
|
-
|
|
26
|
-
Intrig stateful hooks expose a **NetworkState** plus **actions** to fetch / clear.
|
|
27
|
-
Use this when you want a cached, reusable result tied to a global store.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## TL;DR (copy–paste)
|
|
32
|
-
|
|
33
|
-
\`\`\`tsx
|
|
34
|
-
import { useGetUser } from "@intrig/react/users/client";
|
|
35
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
36
|
-
import { useEffect, useMemo } from "react";
|
|
37
|
-
|
|
38
|
-
export default function Example() {
|
|
39
|
-
const [getUserResp, getUser] = useGetUser({ clearOnUnmount: true });
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
getUser({});
|
|
43
|
-
}, [getUser]);
|
|
44
|
-
|
|
45
|
-
const getUserData = useMemo(
|
|
46
|
-
() => (isSuccess(getUserResp) ? getUserResp.data : undefined),
|
|
47
|
-
[getUserResp],
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
if (isPending(getUserResp)) return <>Loading…</>;
|
|
51
|
-
if (isError(getUserResp)) return <>Error: {String(getUserResp.error)}</>;
|
|
52
|
-
return <pre>{JSON.stringify(getUserData, null, 2)}</pre>;
|
|
53
|
-
}
|
|
54
|
-
\`\`\`
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## Hook API
|
|
59
|
-
|
|
60
|
-
\`\`\`ts
|
|
61
|
-
// Options are consistent across hooks.
|
|
62
|
-
type UseHookOptions = {
|
|
63
|
-
/** Execute once after mount with provided params/body (if required). */
|
|
64
|
-
fetchOnMount?: boolean;
|
|
65
|
-
/** Reset the state on unmount (recommended). */
|
|
66
|
-
clearOnUnmount?: boolean;
|
|
67
|
-
/** Distinguish multiple instances of the same hook. */
|
|
68
|
-
key?: string;
|
|
69
|
-
/** Initial path params for endpoints that require them. */
|
|
70
|
-
params?: unknown;
|
|
71
|
-
/** Initial request body (for POST/PUT/etc.). */
|
|
72
|
-
body?: unknown;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Prefer concrete types if your build emits them:
|
|
76
|
-
// import type { GetUserResponseBody } from '@intrig/react/users/GetUser.response';
|
|
77
|
-
|
|
78
|
-
type GetUserData = GetUserResponseBody; // replace with GetUserResponseBody if generated
|
|
79
|
-
type GetUserRequest = { params?: unknown; body?: unknown };
|
|
80
|
-
|
|
81
|
-
// Signature (shape shown; concrete generics vary per generated hook)
|
|
82
|
-
declare function useGetUser(
|
|
83
|
-
options?: UseHookOptions,
|
|
84
|
-
): [NetworkState<GetUserData>, (req: GetUserRequest) => void, () => void];
|
|
85
|
-
\`\`\`
|
|
86
|
-
|
|
87
|
-
### NetworkState & guards
|
|
88
|
-
|
|
89
|
-
\`\`\`ts
|
|
90
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
91
|
-
\`\`\`
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Conceptual model (Stateful = single source of truth)
|
|
96
|
-
|
|
97
|
-
- Lives in a shared store keyed by \`key\` + request signature.
|
|
98
|
-
- Best for **read** endpoints you want to **reuse** or keep **warm** (lists, details, search).
|
|
99
|
-
- Lifecycle helpers:
|
|
100
|
-
- \`fetchOnMount\` to kick off automatically.
|
|
101
|
-
- \`clearOnUnmount\` to clean up (recommended default).
|
|
102
|
-
- \`key\` to isolate multiple independent instances.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Usage patterns
|
|
107
|
-
|
|
108
|
-
### 1) Controlled (most explicit)
|
|
109
|
-
|
|
110
|
-
\`\`\`tsx
|
|
111
|
-
const [getUserResp, getUser, clearGetUser] = useGetUser();
|
|
112
|
-
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
getUser({});
|
|
115
|
-
return clearGetUser; // optional cleanup
|
|
116
|
-
}, [getUser, clearGetUser]);
|
|
117
|
-
\`\`\`
|
|
118
|
-
|
|
119
|
-
<details><summary>Description</summary>
|
|
120
|
-
<p><strong>Use when</strong> you need explicit control over when a request fires, what params/body it uses, and when to clean up. Ideal for search forms, pagination, conditional fetches.</p>
|
|
121
|
-
</details>
|
|
122
|
-
|
|
123
|
-
### 2) Lifecycle-bound (shorthand)
|
|
124
|
-
|
|
125
|
-
\`\`\`tsx
|
|
126
|
-
const [getUserResp] = useGetUser({
|
|
127
|
-
fetchOnMount: true,
|
|
128
|
-
clearOnUnmount: true,
|
|
129
|
-
params: {},
|
|
130
|
-
});
|
|
131
|
-
\`\`\`
|
|
132
|
-
|
|
133
|
-
<details><summary>Description</summary>
|
|
134
|
-
<p><strong>Use when</strong> the data should follow the component lifecycle: fetch once on mount, reset on unmount.</p>
|
|
135
|
-
</details>
|
|
136
|
-
|
|
137
|
-
### 3) Passive observer (render when data arrives)
|
|
138
|
-
|
|
139
|
-
\`\`\`tsx
|
|
140
|
-
const [getUserResp] = useGetUser();
|
|
141
|
-
return isSuccess(getUserResp) ? <>{String(getUserResp.data)}</> : null;
|
|
142
|
-
\`\`\`
|
|
143
|
-
|
|
144
|
-
<details><summary>Description</summary>
|
|
145
|
-
<p><strong>Use when</strong> another part of the app triggers the fetch and this component only reads the state.</p>
|
|
146
|
-
</details>
|
|
147
|
-
|
|
148
|
-
### 4) Keep previous success while refetching (sticky)
|
|
149
|
-
|
|
150
|
-
\`\`\`tsx
|
|
151
|
-
const [getUserData, setGetUserData] = useState<any>();
|
|
152
|
-
const [getUserResp, getUser] = useGetUser();
|
|
153
|
-
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
if (isSuccess(getUserResp)) setGetUserData(getUserResp.data);
|
|
156
|
-
}, [getUserResp]);
|
|
157
|
-
|
|
158
|
-
return (
|
|
159
|
-
<>
|
|
160
|
-
{isPending(getUserResp) && getUserData ? <div>Refreshing…</div> : null}
|
|
161
|
-
<pre>
|
|
162
|
-
{JSON.stringify(
|
|
163
|
-
isSuccess(getUserResp) ? getUserResp.data : getUserData,
|
|
164
|
-
null,
|
|
165
|
-
2,
|
|
166
|
-
)}
|
|
167
|
-
</pre>
|
|
168
|
-
</>
|
|
169
|
-
);
|
|
170
|
-
\`\`\`
|
|
171
|
-
|
|
172
|
-
<details><summary>Description</summary>
|
|
173
|
-
<p><strong>Use when</strong> you want SWR-like UX without flicker.</p>
|
|
174
|
-
</details>
|
|
175
|
-
|
|
176
|
-
### 5) Multiple instances of the same hook (isolate with \`key\`)
|
|
177
|
-
|
|
178
|
-
\`\`\`tsx
|
|
179
|
-
const a = useGetUser({ key: "A", fetchOnMount: true, params: {} });
|
|
180
|
-
const b = useGetUser({ key: "B", fetchOnMount: true, params: {} });
|
|
181
|
-
\`\`\`
|
|
182
|
-
|
|
183
|
-
<details><summary>Description</summary>
|
|
184
|
-
<p>Use unique keys to prevent state collisions.</p>
|
|
185
|
-
</details>
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Before / After mini-migrations
|
|
190
|
-
|
|
191
|
-
### If you mistakenly used Stateful for a simple submit → switch to Async
|
|
192
|
-
|
|
193
|
-
\`\`\`diff
|
|
194
|
-
- const [getUserResp, getUser] = useGetUser();
|
|
195
|
-
- getUser({});
|
|
196
|
-
+ const [fn] = useGetUserAsync();
|
|
197
|
-
+ await fn();
|
|
198
|
-
\`\`\`
|
|
199
|
-
|
|
200
|
-
### If you started with Async but need to read later in another component → Stateful
|
|
201
|
-
|
|
202
|
-
\`\`\`diff
|
|
203
|
-
- const [fn] = useGetUserAsync();
|
|
204
|
-
- const data = await fn();
|
|
205
|
-
+ const [getUserResp, getUser] = useGetUser({ fetchOnMount: true, clearOnUnmount: true, params: {}, });
|
|
206
|
-
+ // read from getUserResp anywhere with useGetUser()
|
|
207
|
-
\`\`\`
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## Anti-patterns
|
|
212
|
-
|
|
213
|
-
<details><summary>Don’t use Stateful for field validations or a one-off submit</summary>
|
|
214
|
-
Use the Async variant instead: \`const [fn] = useGetUserAsync()\`.
|
|
215
|
-
</details>
|
|
216
|
-
<details><summary>Don’t use Async for long-lived lists or detail views</summary>
|
|
217
|
-
Use Stateful so other components can read the same data and you can avoid refetch churn.
|
|
218
|
-
</details>
|
|
219
|
-
<details><summary>Don’t forget required \`params\` when using \`fetchOnMount\`</summary>
|
|
220
|
-
Provide \`params\` (and \`body\` if applicable) or switch to the controlled pattern.
|
|
221
|
-
</details>
|
|
222
|
-
<details><summary>Rendering the same hook twice without a \`key\`</summary>
|
|
223
|
-
If they should be independent, add a unique \`key\`.
|
|
224
|
-
</details>
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## Error & UX guidance
|
|
229
|
-
|
|
230
|
-
- **Loading:** early return or inline spinner. Prefer **sticky data** to avoid blanking content.
|
|
231
|
-
- **Errors:** show a banner or inline errors depending on UX; keep previous good state if possible.
|
|
232
|
-
- **Cleanup:** prefer \`clearOnUnmount: true\` as the default.
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## Concurrency patterns
|
|
237
|
-
|
|
238
|
-
- **Refresh:** call the action again; combine with sticky data for smooth UX.
|
|
239
|
-
- **Dedupe:** isolate instances with \`key\`.
|
|
240
|
-
- **Parallel:** render two keyed instances; don’t share the same key.
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## Full examples
|
|
245
|
-
|
|
246
|
-
### Short format (lifecycle-bound)
|
|
247
|
-
|
|
248
|
-
\`\`\`tsx
|
|
249
|
-
import { useGetUser } from "@intrig/react/users/client";
|
|
250
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
251
|
-
import { useMemo } from "react";
|
|
252
|
-
|
|
253
|
-
function ShortExample() {
|
|
254
|
-
const [getUserResp] = useGetUser({
|
|
255
|
-
fetchOnMount: true,
|
|
256
|
-
clearOnUnmount: true,
|
|
257
|
-
params: {},
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
const getUserData = useMemo(
|
|
261
|
-
() => (isSuccess(getUserResp) ? getUserResp.data : undefined),
|
|
262
|
-
[getUserResp],
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
if (isPending(getUserResp)) return <>Loading…</>;
|
|
266
|
-
if (isError(getUserResp)) return <>Error: {String(getUserResp.error)}</>;
|
|
267
|
-
return <pre>{JSON.stringify(getUserData, null, 2)}</pre>;
|
|
268
|
-
}
|
|
269
|
-
\`\`\`
|
|
270
|
-
|
|
271
|
-
<details><summary>Description</summary>
|
|
272
|
-
<p>Compact lifecycle-bound approach. Great for read-only pages that load once and clean up on unmount.</p>
|
|
273
|
-
</details>
|
|
274
|
-
|
|
275
|
-
### Controlled format (explicit actions)
|
|
276
|
-
|
|
277
|
-
\`\`\`tsx
|
|
278
|
-
import { useGetUser } from "@intrig/react/users/client";
|
|
279
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
280
|
-
import { useEffect, useMemo } from "react";
|
|
281
|
-
|
|
282
|
-
function ControlledExample() {
|
|
283
|
-
const [getUserResp, getUser, clearGetUser] = useGetUser();
|
|
284
|
-
|
|
285
|
-
useEffect(() => {
|
|
286
|
-
getUser({});
|
|
287
|
-
return clearGetUser;
|
|
288
|
-
}, [getUser, clearGetUser]);
|
|
289
|
-
|
|
290
|
-
const getUserData = useMemo(
|
|
291
|
-
() => (isSuccess(getUserResp) ? getUserResp.data : undefined),
|
|
292
|
-
[getUserResp],
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
if (isPending(getUserResp)) return <>Loading…</>;
|
|
296
|
-
if (isError(getUserResp))
|
|
297
|
-
return <>An error occurred: {String(getUserResp.error)}</>;
|
|
298
|
-
return <pre>{JSON.stringify(getUserData, null, 2)}</pre>;
|
|
299
|
-
}
|
|
300
|
-
\`\`\`
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
## Gotchas & Tips
|
|
305
|
-
|
|
306
|
-
- Prefer **\`clearOnUnmount: true\`** in most components.
|
|
307
|
-
- Use **\`key\`** for multiple independent instances.
|
|
308
|
-
- Memoize derived values with **\`useMemo\`** to avoid churn.
|
|
309
|
-
- Inline indicators keep the rest of the page interactive.
|
|
310
|
-
|
|
311
|
-
---
|
|
312
|
-
|
|
313
|
-
## Reference: State helpers
|
|
314
|
-
|
|
315
|
-
\`\`\`ts
|
|
316
|
-
if (isPending(getUserResp)) {
|
|
317
|
-
/* show spinner */
|
|
318
|
-
}
|
|
319
|
-
if (isError(getUserResp)) {
|
|
320
|
-
/* show error */
|
|
321
|
-
}
|
|
322
|
-
if (isSuccess(getUserResp)) {
|
|
323
|
-
/* read getUserResp.data */
|
|
324
|
-
}
|
|
325
|
-
\`\`\`
|
|
326
|
-
"
|
|
327
|
-
`;
|
|
328
|
-
|
|
329
|
-
exports[`reactHookDocs > snapshot — path params only (no request body) 1`] = `
|
|
330
|
-
"# Intrig React Hooks — Quick Guide
|
|
331
|
-
|
|
332
|
-
## Copy-paste starter (fast lane)
|
|
333
|
-
|
|
334
|
-
### 1) Hook import
|
|
335
|
-
|
|
336
|
-
\`\`\`ts
|
|
337
|
-
import { useGetUserById } from "@intrig/react/users/{id}/client";
|
|
338
|
-
\`\`\`
|
|
339
|
-
|
|
340
|
-
### 2) Utility guards
|
|
341
|
-
|
|
342
|
-
\`\`\`ts
|
|
343
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
344
|
-
\`\`\`
|
|
345
|
-
|
|
346
|
-
### 3) Hook instance (auto-clear on unmount)
|
|
347
|
-
|
|
348
|
-
\`\`\`ts
|
|
349
|
-
const [getUserByIdResp, getUserById] = useGetUserById({ clearOnUnmount: true });
|
|
350
|
-
\`\`\`
|
|
351
|
-
|
|
352
|
-
Intrig stateful hooks expose a **NetworkState** plus **actions** to fetch / clear.
|
|
353
|
-
Use this when you want a cached, reusable result tied to a global store.
|
|
354
|
-
|
|
355
|
-
---
|
|
356
|
-
|
|
357
|
-
## TL;DR (copy–paste)
|
|
358
|
-
|
|
359
|
-
\`\`\`tsx
|
|
360
|
-
import { useGetUserById } from "@intrig/react/users/{id}/client";
|
|
361
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
362
|
-
import { useEffect, useMemo } from "react";
|
|
363
|
-
|
|
364
|
-
export default function Example() {
|
|
365
|
-
const [getUserByIdResp, getUserById] = useGetUserById({
|
|
366
|
-
clearOnUnmount: true,
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
useEffect(() => {
|
|
370
|
-
getUserById(getUserByIdParams);
|
|
371
|
-
}, [getUserById, getUserByIdParams]);
|
|
372
|
-
|
|
373
|
-
const getUserByIdData = useMemo(
|
|
374
|
-
() => (isSuccess(getUserByIdResp) ? getUserByIdResp.data : undefined),
|
|
375
|
-
[getUserByIdResp],
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
if (isPending(getUserByIdResp)) return <>Loading…</>;
|
|
379
|
-
if (isError(getUserByIdResp))
|
|
380
|
-
return <>Error: {String(getUserByIdResp.error)}</>;
|
|
381
|
-
return <pre>{JSON.stringify(getUserByIdData, null, 2)}</pre>;
|
|
382
|
-
}
|
|
383
|
-
\`\`\`
|
|
384
|
-
|
|
385
|
-
### Optional types (if generated by your build)
|
|
386
|
-
|
|
387
|
-
\`\`\`ts
|
|
388
|
-
import type { GetUserByIdParams } from "@intrig/react/users/{id}/GetUserById.params";
|
|
389
|
-
// Prefer the concrete response type:
|
|
390
|
-
import type { GetUserByIdResponseBody } from "@intrig/react/users/{id}/GetUserById.response";
|
|
391
|
-
\`\`\`
|
|
392
|
-
|
|
393
|
-
---
|
|
394
|
-
|
|
395
|
-
## Hook API
|
|
396
|
-
|
|
397
|
-
\`\`\`ts
|
|
398
|
-
// Options are consistent across hooks.
|
|
399
|
-
type UseHookOptions = {
|
|
400
|
-
/** Execute once after mount with provided params/body (if required). */
|
|
401
|
-
fetchOnMount?: boolean;
|
|
402
|
-
/** Reset the state on unmount (recommended). */
|
|
403
|
-
clearOnUnmount?: boolean;
|
|
404
|
-
/** Distinguish multiple instances of the same hook. */
|
|
405
|
-
key?: string;
|
|
406
|
-
/** Initial path params for endpoints that require them. */
|
|
407
|
-
params?: GetUserByIdParams;
|
|
408
|
-
/** Initial request body (for POST/PUT/etc.). */
|
|
409
|
-
body?: unknown;
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
// Prefer concrete types if your build emits them:
|
|
413
|
-
// import type { GetUserByIdResponseBody } from '@intrig/react/users/{id}/GetUserById.response';
|
|
414
|
-
|
|
415
|
-
type GetUserByIdData = GetUserByIdResponseBody; // replace with GetUserByIdResponseBody if generated
|
|
416
|
-
type GetUserByIdRequest = { params?: GetUserByIdParams; body?: unknown };
|
|
417
|
-
|
|
418
|
-
// Signature (shape shown; concrete generics vary per generated hook)
|
|
419
|
-
declare function useGetUserById(
|
|
420
|
-
options?: UseHookOptions,
|
|
421
|
-
): [
|
|
422
|
-
NetworkState<GetUserByIdData>,
|
|
423
|
-
(req: GetUserByIdRequest) => void,
|
|
424
|
-
() => void,
|
|
425
|
-
];
|
|
426
|
-
\`\`\`
|
|
427
|
-
|
|
428
|
-
### NetworkState & guards
|
|
429
|
-
|
|
430
|
-
\`\`\`ts
|
|
431
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
432
|
-
\`\`\`
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
|
-
## Conceptual model (Stateful = single source of truth)
|
|
437
|
-
|
|
438
|
-
- Lives in a shared store keyed by \`key\` + request signature.
|
|
439
|
-
- Best for **read** endpoints you want to **reuse** or keep **warm** (lists, details, search).
|
|
440
|
-
- Lifecycle helpers:
|
|
441
|
-
- \`fetchOnMount\` to kick off automatically.
|
|
442
|
-
- \`clearOnUnmount\` to clean up (recommended default).
|
|
443
|
-
- \`key\` to isolate multiple independent instances.
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Usage patterns
|
|
448
|
-
|
|
449
|
-
### 1) Controlled (most explicit)
|
|
450
|
-
|
|
451
|
-
\`\`\`tsx
|
|
452
|
-
const [getUserByIdResp, getUserById, clearGetUserById] = useGetUserById();
|
|
453
|
-
|
|
454
|
-
useEffect(() => {
|
|
455
|
-
getUserById(getUserByIdParams);
|
|
456
|
-
return clearGetUserById; // optional cleanup
|
|
457
|
-
}, [getUserById, clearGetUserById]);
|
|
458
|
-
\`\`\`
|
|
459
|
-
|
|
460
|
-
<details><summary>Description</summary>
|
|
461
|
-
<p><strong>Use when</strong> you need explicit control over when a request fires, what params/body it uses, and when to clean up. Ideal for search forms, pagination, conditional fetches.</p>
|
|
462
|
-
</details>
|
|
463
|
-
|
|
464
|
-
### 2) Lifecycle-bound (shorthand)
|
|
465
|
-
|
|
466
|
-
\`\`\`tsx
|
|
467
|
-
const [getUserByIdResp] = useGetUserById({
|
|
468
|
-
fetchOnMount: true,
|
|
469
|
-
clearOnUnmount: true,
|
|
470
|
-
params: getUserByIdParams,
|
|
471
|
-
});
|
|
472
|
-
\`\`\`
|
|
473
|
-
|
|
474
|
-
<details><summary>Description</summary>
|
|
475
|
-
<p><strong>Use when</strong> the data should follow the component lifecycle: fetch once on mount, reset on unmount.</p>
|
|
476
|
-
</details>
|
|
477
|
-
|
|
478
|
-
### 3) Passive observer (render when data arrives)
|
|
479
|
-
|
|
480
|
-
\`\`\`tsx
|
|
481
|
-
const [getUserByIdResp] = useGetUserById();
|
|
482
|
-
return isSuccess(getUserByIdResp) ? <>{String(getUserByIdResp.data)}</> : null;
|
|
483
|
-
\`\`\`
|
|
484
|
-
|
|
485
|
-
<details><summary>Description</summary>
|
|
486
|
-
<p><strong>Use when</strong> another part of the app triggers the fetch and this component only reads the state.</p>
|
|
487
|
-
</details>
|
|
488
|
-
|
|
489
|
-
### 4) Keep previous success while refetching (sticky)
|
|
490
|
-
|
|
491
|
-
\`\`\`tsx
|
|
492
|
-
const [getUserByIdData, setGetUserByIdData] = useState<any>();
|
|
493
|
-
const [getUserByIdResp, getUserById] = useGetUserById();
|
|
494
|
-
|
|
495
|
-
useEffect(() => {
|
|
496
|
-
if (isSuccess(getUserByIdResp)) setGetUserByIdData(getUserByIdResp.data);
|
|
497
|
-
}, [getUserByIdResp]);
|
|
498
|
-
|
|
499
|
-
return (
|
|
500
|
-
<>
|
|
501
|
-
{isPending(getUserByIdResp) && getUserByIdData ? (
|
|
502
|
-
<div>Refreshing…</div>
|
|
503
|
-
) : null}
|
|
504
|
-
<pre>
|
|
505
|
-
{JSON.stringify(
|
|
506
|
-
isSuccess(getUserByIdResp) ? getUserByIdResp.data : getUserByIdData,
|
|
507
|
-
null,
|
|
508
|
-
2,
|
|
509
|
-
)}
|
|
510
|
-
</pre>
|
|
511
|
-
</>
|
|
512
|
-
);
|
|
513
|
-
\`\`\`
|
|
514
|
-
|
|
515
|
-
<details><summary>Description</summary>
|
|
516
|
-
<p><strong>Use when</strong> you want SWR-like UX without flicker.</p>
|
|
517
|
-
</details>
|
|
518
|
-
|
|
519
|
-
### 5) Multiple instances of the same hook (isolate with \`key\`)
|
|
520
|
-
|
|
521
|
-
\`\`\`tsx
|
|
522
|
-
const a = useGetUserById({
|
|
523
|
-
key: "A",
|
|
524
|
-
fetchOnMount: true,
|
|
525
|
-
params: getUserByIdParams,
|
|
526
|
-
});
|
|
527
|
-
const b = useGetUserById({
|
|
528
|
-
key: "B",
|
|
529
|
-
fetchOnMount: true,
|
|
530
|
-
params: getUserByIdParams,
|
|
531
|
-
});
|
|
532
|
-
\`\`\`
|
|
533
|
-
|
|
534
|
-
<details><summary>Description</summary>
|
|
535
|
-
<p>Use unique keys to prevent state collisions.</p>
|
|
536
|
-
</details>
|
|
537
|
-
|
|
538
|
-
---
|
|
539
|
-
|
|
540
|
-
## Before / After mini-migrations
|
|
541
|
-
|
|
542
|
-
### If you mistakenly used Stateful for a simple submit → switch to Async
|
|
543
|
-
|
|
544
|
-
\`\`\`diff
|
|
545
|
-
- const [getUserByIdResp, getUserById] = useGetUserById();
|
|
546
|
-
- getUserById(getUserByIdParams);
|
|
547
|
-
+ const [fn] = useGetUserByIdAsync();
|
|
548
|
-
+ await fn(getUserByIdParams);
|
|
549
|
-
\`\`\`
|
|
550
|
-
|
|
551
|
-
### If you started with Async but need to read later in another component → Stateful
|
|
552
|
-
|
|
553
|
-
\`\`\`diff
|
|
554
|
-
- const [fn] = useGetUserByIdAsync();
|
|
555
|
-
- const data = await fn(getUserByIdParams);
|
|
556
|
-
+ const [getUserByIdResp, getUserById] = useGetUserById({ fetchOnMount: true, clearOnUnmount: true, params: getUserByIdParams, });
|
|
557
|
-
+ // read from getUserByIdResp anywhere with useGetUserById()
|
|
558
|
-
\`\`\`
|
|
559
|
-
|
|
560
|
-
---
|
|
561
|
-
|
|
562
|
-
## Anti-patterns
|
|
563
|
-
|
|
564
|
-
<details><summary>Don’t use Stateful for field validations or a one-off submit</summary>
|
|
565
|
-
Use the Async variant instead: \`const [fn] = useGetUserByIdAsync()\`.
|
|
566
|
-
</details>
|
|
567
|
-
<details><summary>Don’t use Async for long-lived lists or detail views</summary>
|
|
568
|
-
Use Stateful so other components can read the same data and you can avoid refetch churn.
|
|
569
|
-
</details>
|
|
570
|
-
<details><summary>Don’t forget required \`params\` when using \`fetchOnMount\`</summary>
|
|
571
|
-
Provide \`params\` (and \`body\` if applicable) or switch to the controlled pattern.
|
|
572
|
-
</details>
|
|
573
|
-
<details><summary>Rendering the same hook twice without a \`key\`</summary>
|
|
574
|
-
If they should be independent, add a unique \`key\`.
|
|
575
|
-
</details>
|
|
576
|
-
|
|
577
|
-
---
|
|
578
|
-
|
|
579
|
-
## Error & UX guidance
|
|
580
|
-
|
|
581
|
-
- **Loading:** early return or inline spinner. Prefer **sticky data** to avoid blanking content.
|
|
582
|
-
- **Errors:** show a banner or inline errors depending on UX; keep previous good state if possible.
|
|
583
|
-
- **Cleanup:** prefer \`clearOnUnmount: true\` as the default.
|
|
584
|
-
|
|
585
|
-
---
|
|
586
|
-
|
|
587
|
-
## Concurrency patterns
|
|
588
|
-
|
|
589
|
-
- **Refresh:** call the action again; combine with sticky data for smooth UX.
|
|
590
|
-
- **Dedupe:** isolate instances with \`key\`.
|
|
591
|
-
- **Parallel:** render two keyed instances; don’t share the same key.
|
|
592
|
-
|
|
593
|
-
---
|
|
594
|
-
|
|
595
|
-
## Full examples
|
|
596
|
-
|
|
597
|
-
### Short format (lifecycle-bound)
|
|
598
|
-
|
|
599
|
-
\`\`\`tsx
|
|
600
|
-
import { useGetUserById } from "@intrig/react/users/{id}/client";
|
|
601
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
602
|
-
import { useMemo } from "react";
|
|
603
|
-
|
|
604
|
-
function ShortExample() {
|
|
605
|
-
const [getUserByIdResp] = useGetUserById({
|
|
606
|
-
fetchOnMount: true,
|
|
607
|
-
clearOnUnmount: true,
|
|
608
|
-
params: getUserByIdParams,
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
const getUserByIdData = useMemo(
|
|
612
|
-
() => (isSuccess(getUserByIdResp) ? getUserByIdResp.data : undefined),
|
|
613
|
-
[getUserByIdResp],
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
if (isPending(getUserByIdResp)) return <>Loading…</>;
|
|
617
|
-
if (isError(getUserByIdResp))
|
|
618
|
-
return <>Error: {String(getUserByIdResp.error)}</>;
|
|
619
|
-
return <pre>{JSON.stringify(getUserByIdData, null, 2)}</pre>;
|
|
620
|
-
}
|
|
621
|
-
\`\`\`
|
|
622
|
-
|
|
623
|
-
<details><summary>Description</summary>
|
|
624
|
-
<p>Compact lifecycle-bound approach. Great for read-only pages that load once and clean up on unmount.</p>
|
|
625
|
-
</details>
|
|
626
|
-
|
|
627
|
-
### Controlled format (explicit actions)
|
|
628
|
-
|
|
629
|
-
\`\`\`tsx
|
|
630
|
-
import { useGetUserById } from "@intrig/react/users/{id}/client";
|
|
631
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
632
|
-
import { useEffect, useMemo } from "react";
|
|
633
|
-
|
|
634
|
-
function ControlledExample() {
|
|
635
|
-
const [getUserByIdResp, getUserById, clearGetUserById] = useGetUserById();
|
|
636
|
-
|
|
637
|
-
useEffect(() => {
|
|
638
|
-
getUserById(getUserByIdParams);
|
|
639
|
-
return clearGetUserById;
|
|
640
|
-
}, [getUserById, clearGetUserById]);
|
|
641
|
-
|
|
642
|
-
const getUserByIdData = useMemo(
|
|
643
|
-
() => (isSuccess(getUserByIdResp) ? getUserByIdResp.data : undefined),
|
|
644
|
-
[getUserByIdResp],
|
|
645
|
-
);
|
|
646
|
-
|
|
647
|
-
if (isPending(getUserByIdResp)) return <>Loading…</>;
|
|
648
|
-
if (isError(getUserByIdResp))
|
|
649
|
-
return <>An error occurred: {String(getUserByIdResp.error)}</>;
|
|
650
|
-
return <pre>{JSON.stringify(getUserByIdData, null, 2)}</pre>;
|
|
651
|
-
}
|
|
652
|
-
\`\`\`
|
|
653
|
-
|
|
654
|
-
---
|
|
655
|
-
|
|
656
|
-
## Gotchas & Tips
|
|
657
|
-
|
|
658
|
-
- Prefer **\`clearOnUnmount: true\`** in most components.
|
|
659
|
-
- Use **\`key\`** for multiple independent instances.
|
|
660
|
-
- Memoize derived values with **\`useMemo\`** to avoid churn.
|
|
661
|
-
- Inline indicators keep the rest of the page interactive.
|
|
662
|
-
|
|
663
|
-
---
|
|
664
|
-
|
|
665
|
-
## Reference: State helpers
|
|
666
|
-
|
|
667
|
-
\`\`\`ts
|
|
668
|
-
if (isPending(getUserByIdResp)) {
|
|
669
|
-
/* show spinner */
|
|
670
|
-
}
|
|
671
|
-
if (isError(getUserByIdResp)) {
|
|
672
|
-
/* show error */
|
|
673
|
-
}
|
|
674
|
-
if (isSuccess(getUserByIdResp)) {
|
|
675
|
-
/* read getUserByIdResp.data */
|
|
676
|
-
}
|
|
677
|
-
\`\`\`
|
|
678
|
-
"
|
|
679
|
-
`;
|
|
680
|
-
|
|
681
|
-
exports[`reactHookDocs > snapshot — request body and path params 1`] = `
|
|
682
|
-
"# Intrig React Hooks — Quick Guide
|
|
683
|
-
|
|
684
|
-
## Copy-paste starter (fast lane)
|
|
685
|
-
|
|
686
|
-
### 1) Hook import
|
|
687
|
-
|
|
688
|
-
\`\`\`ts
|
|
689
|
-
import { useUpdateUser } from "@intrig/react/users/{id}/client";
|
|
690
|
-
\`\`\`
|
|
691
|
-
|
|
692
|
-
### 2) Utility guards
|
|
693
|
-
|
|
694
|
-
\`\`\`ts
|
|
695
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
696
|
-
\`\`\`
|
|
697
|
-
|
|
698
|
-
### 3) Hook instance (auto-clear on unmount)
|
|
699
|
-
|
|
700
|
-
\`\`\`ts
|
|
701
|
-
const [updateUserResp, updateUser] = useUpdateUser({ clearOnUnmount: true });
|
|
702
|
-
\`\`\`
|
|
703
|
-
|
|
704
|
-
Intrig stateful hooks expose a **NetworkState** plus **actions** to fetch / clear.
|
|
705
|
-
Use this when you want a cached, reusable result tied to a global store.
|
|
706
|
-
|
|
707
|
-
---
|
|
708
|
-
|
|
709
|
-
## TL;DR (copy–paste)
|
|
710
|
-
|
|
711
|
-
\`\`\`tsx
|
|
712
|
-
import { useUpdateUser } from "@intrig/react/users/{id}/client";
|
|
713
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
714
|
-
import { useEffect, useMemo } from "react";
|
|
715
|
-
|
|
716
|
-
export default function Example() {
|
|
717
|
-
const [updateUserResp, updateUser] = useUpdateUser({ clearOnUnmount: true });
|
|
718
|
-
|
|
719
|
-
useEffect(() => {
|
|
720
|
-
updateUser(updateUserRequest, updateUserParams);
|
|
721
|
-
}, [updateUser, updateUserRequest, updateUserParams]);
|
|
722
|
-
|
|
723
|
-
const updateUserData = useMemo(
|
|
724
|
-
() => (isSuccess(updateUserResp) ? updateUserResp.data : undefined),
|
|
725
|
-
[updateUserResp],
|
|
726
|
-
);
|
|
727
|
-
|
|
728
|
-
if (isPending(updateUserResp)) return <>Loading…</>;
|
|
729
|
-
if (isError(updateUserResp))
|
|
730
|
-
return <>Error: {String(updateUserResp.error)}</>;
|
|
731
|
-
return <pre>{JSON.stringify(updateUserData, null, 2)}</pre>;
|
|
732
|
-
}
|
|
733
|
-
\`\`\`
|
|
734
|
-
|
|
735
|
-
### Optional types (if generated by your build)
|
|
736
|
-
|
|
737
|
-
\`\`\`ts
|
|
738
|
-
import type { UpdateUserRequest } from "@intrig/react/demo_api/components/schemas/UpdateUserRequest";
|
|
739
|
-
import type { UpdateUserParams } from "@intrig/react/users/{id}/UpdateUser.params";
|
|
740
|
-
// Prefer the concrete response type:
|
|
741
|
-
import type { UpdateUserResponseBody } from "@intrig/react/users/{id}/UpdateUser.response";
|
|
742
|
-
\`\`\`
|
|
743
|
-
|
|
744
|
-
---
|
|
745
|
-
|
|
746
|
-
## Hook API
|
|
747
|
-
|
|
748
|
-
\`\`\`ts
|
|
749
|
-
// Options are consistent across hooks.
|
|
750
|
-
type UseHookOptions = {
|
|
751
|
-
/** Execute once after mount with provided params/body (if required). */
|
|
752
|
-
fetchOnMount?: boolean;
|
|
753
|
-
/** Reset the state on unmount (recommended). */
|
|
754
|
-
clearOnUnmount?: boolean;
|
|
755
|
-
/** Distinguish multiple instances of the same hook. */
|
|
756
|
-
key?: string;
|
|
757
|
-
/** Initial path params for endpoints that require them. */
|
|
758
|
-
params?: UpdateUserParams;
|
|
759
|
-
/** Initial request body (for POST/PUT/etc.). */
|
|
760
|
-
body?: UpdateUserRequest;
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
// Prefer concrete types if your build emits them:
|
|
764
|
-
// import type { UpdateUserResponseBody } from '@intrig/react/users/{id}/UpdateUser.response';
|
|
765
|
-
|
|
766
|
-
type UpdateUserData = UpdateUserResponseBody; // replace with UpdateUserResponseBody if generated
|
|
767
|
-
type UpdateUserRequest = {
|
|
768
|
-
params?: UpdateUserParams;
|
|
769
|
-
body?: UpdateUserRequest;
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
// Signature (shape shown; concrete generics vary per generated hook)
|
|
773
|
-
declare function useUpdateUser(
|
|
774
|
-
options?: UseHookOptions,
|
|
775
|
-
): [NetworkState<UpdateUserData>, (req: UpdateUserRequest) => void, () => void];
|
|
776
|
-
\`\`\`
|
|
777
|
-
|
|
778
|
-
### NetworkState & guards
|
|
779
|
-
|
|
780
|
-
\`\`\`ts
|
|
781
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
782
|
-
\`\`\`
|
|
783
|
-
|
|
784
|
-
---
|
|
785
|
-
|
|
786
|
-
## Conceptual model (Stateful = single source of truth)
|
|
787
|
-
|
|
788
|
-
- Lives in a shared store keyed by \`key\` + request signature.
|
|
789
|
-
- Best for **read** endpoints you want to **reuse** or keep **warm** (lists, details, search).
|
|
790
|
-
- Lifecycle helpers:
|
|
791
|
-
- \`fetchOnMount\` to kick off automatically.
|
|
792
|
-
- \`clearOnUnmount\` to clean up (recommended default).
|
|
793
|
-
- \`key\` to isolate multiple independent instances.
|
|
794
|
-
|
|
795
|
-
---
|
|
796
|
-
|
|
797
|
-
## Usage patterns
|
|
798
|
-
|
|
799
|
-
### 1) Controlled (most explicit)
|
|
800
|
-
|
|
801
|
-
\`\`\`tsx
|
|
802
|
-
const [updateUserResp, updateUser, clearUpdateUser] = useUpdateUser();
|
|
803
|
-
|
|
804
|
-
useEffect(() => {
|
|
805
|
-
updateUser(updateUserRequest, updateUserParams);
|
|
806
|
-
return clearUpdateUser; // optional cleanup
|
|
807
|
-
}, [updateUser, clearUpdateUser]);
|
|
808
|
-
\`\`\`
|
|
809
|
-
|
|
810
|
-
<details><summary>Description</summary>
|
|
811
|
-
<p><strong>Use when</strong> you need explicit control over when a request fires, what params/body it uses, and when to clean up. Ideal for search forms, pagination, conditional fetches.</p>
|
|
812
|
-
</details>
|
|
813
|
-
|
|
814
|
-
### 2) Lifecycle-bound (shorthand)
|
|
815
|
-
|
|
816
|
-
\`\`\`tsx
|
|
817
|
-
const [updateUserResp] = useUpdateUser({
|
|
818
|
-
fetchOnMount: true,
|
|
819
|
-
clearOnUnmount: true,
|
|
820
|
-
body: updateUserRequest,
|
|
821
|
-
params: updateUserParams,
|
|
822
|
-
});
|
|
823
|
-
\`\`\`
|
|
824
|
-
|
|
825
|
-
<details><summary>Description</summary>
|
|
826
|
-
<p><strong>Use when</strong> the data should follow the component lifecycle: fetch once on mount, reset on unmount.</p>
|
|
827
|
-
</details>
|
|
828
|
-
|
|
829
|
-
### 3) Passive observer (render when data arrives)
|
|
830
|
-
|
|
831
|
-
\`\`\`tsx
|
|
832
|
-
const [updateUserResp] = useUpdateUser();
|
|
833
|
-
return isSuccess(updateUserResp) ? <>{String(updateUserResp.data)}</> : null;
|
|
834
|
-
\`\`\`
|
|
835
|
-
|
|
836
|
-
<details><summary>Description</summary>
|
|
837
|
-
<p><strong>Use when</strong> another part of the app triggers the fetch and this component only reads the state.</p>
|
|
838
|
-
</details>
|
|
839
|
-
|
|
840
|
-
### 4) Keep previous success while refetching (sticky)
|
|
841
|
-
|
|
842
|
-
\`\`\`tsx
|
|
843
|
-
const [updateUserData, setUpdateUserData] = useState<any>();
|
|
844
|
-
const [updateUserResp, updateUser] = useUpdateUser();
|
|
845
|
-
|
|
846
|
-
useEffect(() => {
|
|
847
|
-
if (isSuccess(updateUserResp)) setUpdateUserData(updateUserResp.data);
|
|
848
|
-
}, [updateUserResp]);
|
|
849
|
-
|
|
850
|
-
return (
|
|
851
|
-
<>
|
|
852
|
-
{isPending(updateUserResp) && updateUserData ? (
|
|
853
|
-
<div>Refreshing…</div>
|
|
854
|
-
) : null}
|
|
855
|
-
<pre>
|
|
856
|
-
{JSON.stringify(
|
|
857
|
-
isSuccess(updateUserResp) ? updateUserResp.data : updateUserData,
|
|
858
|
-
null,
|
|
859
|
-
2,
|
|
860
|
-
)}
|
|
861
|
-
</pre>
|
|
862
|
-
</>
|
|
863
|
-
);
|
|
864
|
-
\`\`\`
|
|
865
|
-
|
|
866
|
-
<details><summary>Description</summary>
|
|
867
|
-
<p><strong>Use when</strong> you want SWR-like UX without flicker.</p>
|
|
868
|
-
</details>
|
|
869
|
-
|
|
870
|
-
### 5) Multiple instances of the same hook (isolate with \`key\`)
|
|
871
|
-
|
|
872
|
-
\`\`\`tsx
|
|
873
|
-
const a = useUpdateUser({
|
|
874
|
-
key: "A",
|
|
875
|
-
fetchOnMount: true,
|
|
876
|
-
params: updateUserParams,
|
|
877
|
-
});
|
|
878
|
-
const b = useUpdateUser({
|
|
879
|
-
key: "B",
|
|
880
|
-
fetchOnMount: true,
|
|
881
|
-
params: updateUserParams,
|
|
882
|
-
});
|
|
883
|
-
\`\`\`
|
|
884
|
-
|
|
885
|
-
<details><summary>Description</summary>
|
|
886
|
-
<p>Use unique keys to prevent state collisions.</p>
|
|
887
|
-
</details>
|
|
888
|
-
|
|
889
|
-
---
|
|
890
|
-
|
|
891
|
-
## Before / After mini-migrations
|
|
892
|
-
|
|
893
|
-
### If you mistakenly used Stateful for a simple submit → switch to Async
|
|
894
|
-
|
|
895
|
-
\`\`\`diff
|
|
896
|
-
- const [updateUserResp, updateUser] = useUpdateUser();
|
|
897
|
-
- updateUser(updateUserRequest, updateUserParams);
|
|
898
|
-
+ const [fn] = useUpdateUserAsync();
|
|
899
|
-
+ await fn(updateUserRequest, updateUserParams);
|
|
900
|
-
\`\`\`
|
|
901
|
-
|
|
902
|
-
### If you started with Async but need to read later in another component → Stateful
|
|
903
|
-
|
|
904
|
-
\`\`\`diff
|
|
905
|
-
- const [fn] = useUpdateUserAsync();
|
|
906
|
-
- const data = await fn(updateUserRequest, updateUserParams);
|
|
907
|
-
+ const [updateUserResp, updateUser] = useUpdateUser({ fetchOnMount: true, clearOnUnmount: true, params: updateUserParams, body: updateUserRequest });
|
|
908
|
-
+ // read from updateUserResp anywhere with useUpdateUser()
|
|
909
|
-
\`\`\`
|
|
910
|
-
|
|
911
|
-
---
|
|
912
|
-
|
|
913
|
-
## Anti-patterns
|
|
914
|
-
|
|
915
|
-
<details><summary>Don’t use Stateful for field validations or a one-off submit</summary>
|
|
916
|
-
Use the Async variant instead: \`const [fn] = useUpdateUserAsync()\`.
|
|
917
|
-
</details>
|
|
918
|
-
<details><summary>Don’t use Async for long-lived lists or detail views</summary>
|
|
919
|
-
Use Stateful so other components can read the same data and you can avoid refetch churn.
|
|
920
|
-
</details>
|
|
921
|
-
<details><summary>Don’t forget required \`params\` when using \`fetchOnMount\`</summary>
|
|
922
|
-
Provide \`params\` (and \`body\` if applicable) or switch to the controlled pattern.
|
|
923
|
-
</details>
|
|
924
|
-
<details><summary>Rendering the same hook twice without a \`key\`</summary>
|
|
925
|
-
If they should be independent, add a unique \`key\`.
|
|
926
|
-
</details>
|
|
927
|
-
|
|
928
|
-
---
|
|
929
|
-
|
|
930
|
-
## Error & UX guidance
|
|
931
|
-
|
|
932
|
-
- **Loading:** early return or inline spinner. Prefer **sticky data** to avoid blanking content.
|
|
933
|
-
- **Errors:** show a banner or inline errors depending on UX; keep previous good state if possible.
|
|
934
|
-
- **Cleanup:** prefer \`clearOnUnmount: true\` as the default.
|
|
935
|
-
|
|
936
|
-
---
|
|
937
|
-
|
|
938
|
-
## Concurrency patterns
|
|
939
|
-
|
|
940
|
-
- **Refresh:** call the action again; combine with sticky data for smooth UX.
|
|
941
|
-
- **Dedupe:** isolate instances with \`key\`.
|
|
942
|
-
- **Parallel:** render two keyed instances; don’t share the same key.
|
|
943
|
-
|
|
944
|
-
---
|
|
945
|
-
|
|
946
|
-
## Full examples
|
|
947
|
-
|
|
948
|
-
### Short format (lifecycle-bound)
|
|
949
|
-
|
|
950
|
-
\`\`\`tsx
|
|
951
|
-
import { useUpdateUser } from "@intrig/react/users/{id}/client";
|
|
952
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
953
|
-
import { useMemo } from "react";
|
|
954
|
-
|
|
955
|
-
function ShortExample() {
|
|
956
|
-
const [updateUserResp] = useUpdateUser({
|
|
957
|
-
fetchOnMount: true,
|
|
958
|
-
clearOnUnmount: true,
|
|
959
|
-
body: updateUserRequest,
|
|
960
|
-
params: updateUserParams,
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
const updateUserData = useMemo(
|
|
964
|
-
() => (isSuccess(updateUserResp) ? updateUserResp.data : undefined),
|
|
965
|
-
[updateUserResp],
|
|
966
|
-
);
|
|
967
|
-
|
|
968
|
-
if (isPending(updateUserResp)) return <>Loading…</>;
|
|
969
|
-
if (isError(updateUserResp))
|
|
970
|
-
return <>Error: {String(updateUserResp.error)}</>;
|
|
971
|
-
return <pre>{JSON.stringify(updateUserData, null, 2)}</pre>;
|
|
972
|
-
}
|
|
973
|
-
\`\`\`
|
|
974
|
-
|
|
975
|
-
<details><summary>Description</summary>
|
|
976
|
-
<p>Compact lifecycle-bound approach. Great for read-only pages that load once and clean up on unmount.</p>
|
|
977
|
-
</details>
|
|
978
|
-
|
|
979
|
-
### Controlled format (explicit actions)
|
|
980
|
-
|
|
981
|
-
\`\`\`tsx
|
|
982
|
-
import { useUpdateUser } from "@intrig/react/users/{id}/client";
|
|
983
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
984
|
-
import { useEffect, useMemo } from "react";
|
|
985
|
-
|
|
986
|
-
function ControlledExample() {
|
|
987
|
-
const [updateUserResp, updateUser, clearUpdateUser] = useUpdateUser();
|
|
988
|
-
|
|
989
|
-
useEffect(() => {
|
|
990
|
-
updateUser(updateUserRequest, updateUserParams);
|
|
991
|
-
return clearUpdateUser;
|
|
992
|
-
}, [updateUser, clearUpdateUser]);
|
|
993
|
-
|
|
994
|
-
const updateUserData = useMemo(
|
|
995
|
-
() => (isSuccess(updateUserResp) ? updateUserResp.data : undefined),
|
|
996
|
-
[updateUserResp],
|
|
997
|
-
);
|
|
998
|
-
|
|
999
|
-
if (isPending(updateUserResp)) return <>Loading…</>;
|
|
1000
|
-
if (isError(updateUserResp))
|
|
1001
|
-
return <>An error occurred: {String(updateUserResp.error)}</>;
|
|
1002
|
-
return <pre>{JSON.stringify(updateUserData, null, 2)}</pre>;
|
|
1003
|
-
}
|
|
1004
|
-
\`\`\`
|
|
1005
|
-
|
|
1006
|
-
---
|
|
1007
|
-
|
|
1008
|
-
## Gotchas & Tips
|
|
1009
|
-
|
|
1010
|
-
- Prefer **\`clearOnUnmount: true\`** in most components.
|
|
1011
|
-
- Use **\`key\`** for multiple independent instances.
|
|
1012
|
-
- Memoize derived values with **\`useMemo\`** to avoid churn.
|
|
1013
|
-
- Inline indicators keep the rest of the page interactive.
|
|
1014
|
-
|
|
1015
|
-
---
|
|
1016
|
-
|
|
1017
|
-
## Reference: State helpers
|
|
1018
|
-
|
|
1019
|
-
\`\`\`ts
|
|
1020
|
-
if (isPending(updateUserResp)) {
|
|
1021
|
-
/* show spinner */
|
|
1022
|
-
}
|
|
1023
|
-
if (isError(updateUserResp)) {
|
|
1024
|
-
/* show error */
|
|
1025
|
-
}
|
|
1026
|
-
if (isSuccess(updateUserResp)) {
|
|
1027
|
-
/* read updateUserResp.data */
|
|
1028
|
-
}
|
|
1029
|
-
\`\`\`
|
|
1030
|
-
"
|
|
1031
|
-
`;
|
|
1032
|
-
|
|
1033
|
-
exports[`reactHookDocs > snapshot — request body only (no path params) 1`] = `
|
|
1034
|
-
"# Intrig React Hooks — Quick Guide
|
|
1035
|
-
|
|
1036
|
-
## Copy-paste starter (fast lane)
|
|
1037
|
-
|
|
1038
|
-
### 1) Hook import
|
|
1039
|
-
|
|
1040
|
-
\`\`\`ts
|
|
1041
|
-
import { useCreateUser } from "@intrig/react/users/client";
|
|
1042
|
-
\`\`\`
|
|
1043
|
-
|
|
1044
|
-
### 2) Utility guards
|
|
1045
|
-
|
|
1046
|
-
\`\`\`ts
|
|
1047
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
1048
|
-
\`\`\`
|
|
1049
|
-
|
|
1050
|
-
### 3) Hook instance (auto-clear on unmount)
|
|
1051
|
-
|
|
1052
|
-
\`\`\`ts
|
|
1053
|
-
const [createUserResp, createUser] = useCreateUser({ clearOnUnmount: true });
|
|
1054
|
-
\`\`\`
|
|
1055
|
-
|
|
1056
|
-
Intrig stateful hooks expose a **NetworkState** plus **actions** to fetch / clear.
|
|
1057
|
-
Use this when you want a cached, reusable result tied to a global store.
|
|
1058
|
-
|
|
1059
|
-
---
|
|
1060
|
-
|
|
1061
|
-
## TL;DR (copy–paste)
|
|
1062
|
-
|
|
1063
|
-
\`\`\`tsx
|
|
1064
|
-
import { useCreateUser } from "@intrig/react/users/client";
|
|
1065
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
1066
|
-
import { useEffect, useMemo } from "react";
|
|
1067
|
-
|
|
1068
|
-
export default function Example() {
|
|
1069
|
-
const [createUserResp, createUser] = useCreateUser({ clearOnUnmount: true });
|
|
1070
|
-
|
|
1071
|
-
useEffect(() => {
|
|
1072
|
-
createUser(createUserRequest, {});
|
|
1073
|
-
}, [createUser, createUserRequest]);
|
|
1074
|
-
|
|
1075
|
-
const createUserData = useMemo(
|
|
1076
|
-
() => (isSuccess(createUserResp) ? createUserResp.data : undefined),
|
|
1077
|
-
[createUserResp],
|
|
1078
|
-
);
|
|
1079
|
-
|
|
1080
|
-
if (isPending(createUserResp)) return <>Loading…</>;
|
|
1081
|
-
if (isError(createUserResp))
|
|
1082
|
-
return <>Error: {String(createUserResp.error)}</>;
|
|
1083
|
-
return <pre>{JSON.stringify(createUserData, null, 2)}</pre>;
|
|
1084
|
-
}
|
|
1085
|
-
\`\`\`
|
|
1086
|
-
|
|
1087
|
-
### Optional types (if generated by your build)
|
|
1088
|
-
|
|
1089
|
-
\`\`\`ts
|
|
1090
|
-
import type { CreateUserRequest } from "@intrig/react/demo_api/components/schemas/CreateUserRequest";
|
|
1091
|
-
// Prefer the concrete response type:
|
|
1092
|
-
import type { CreateUserResponseBody } from "@intrig/react/users/CreateUser.response";
|
|
1093
|
-
\`\`\`
|
|
1094
|
-
|
|
1095
|
-
---
|
|
1096
|
-
|
|
1097
|
-
## Hook API
|
|
1098
|
-
|
|
1099
|
-
\`\`\`ts
|
|
1100
|
-
// Options are consistent across hooks.
|
|
1101
|
-
type UseHookOptions = {
|
|
1102
|
-
/** Execute once after mount with provided params/body (if required). */
|
|
1103
|
-
fetchOnMount?: boolean;
|
|
1104
|
-
/** Reset the state on unmount (recommended). */
|
|
1105
|
-
clearOnUnmount?: boolean;
|
|
1106
|
-
/** Distinguish multiple instances of the same hook. */
|
|
1107
|
-
key?: string;
|
|
1108
|
-
/** Initial path params for endpoints that require them. */
|
|
1109
|
-
params?: unknown;
|
|
1110
|
-
/** Initial request body (for POST/PUT/etc.). */
|
|
1111
|
-
body?: CreateUserRequest;
|
|
1112
|
-
};
|
|
1113
|
-
|
|
1114
|
-
// Prefer concrete types if your build emits them:
|
|
1115
|
-
// import type { CreateUserResponseBody } from '@intrig/react/users/CreateUser.response';
|
|
1116
|
-
|
|
1117
|
-
type CreateUserData = CreateUserResponseBody; // replace with CreateUserResponseBody if generated
|
|
1118
|
-
type CreateUserRequest = { params?: unknown; body?: CreateUserRequest };
|
|
1119
|
-
|
|
1120
|
-
// Signature (shape shown; concrete generics vary per generated hook)
|
|
1121
|
-
declare function useCreateUser(
|
|
1122
|
-
options?: UseHookOptions,
|
|
1123
|
-
): [NetworkState<CreateUserData>, (req: CreateUserRequest) => void, () => void];
|
|
1124
|
-
\`\`\`
|
|
1125
|
-
|
|
1126
|
-
### NetworkState & guards
|
|
1127
|
-
|
|
1128
|
-
\`\`\`ts
|
|
1129
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
1130
|
-
\`\`\`
|
|
1131
|
-
|
|
1132
|
-
---
|
|
1133
|
-
|
|
1134
|
-
## Conceptual model (Stateful = single source of truth)
|
|
1135
|
-
|
|
1136
|
-
- Lives in a shared store keyed by \`key\` + request signature.
|
|
1137
|
-
- Best for **read** endpoints you want to **reuse** or keep **warm** (lists, details, search).
|
|
1138
|
-
- Lifecycle helpers:
|
|
1139
|
-
- \`fetchOnMount\` to kick off automatically.
|
|
1140
|
-
- \`clearOnUnmount\` to clean up (recommended default).
|
|
1141
|
-
- \`key\` to isolate multiple independent instances.
|
|
1142
|
-
|
|
1143
|
-
---
|
|
1144
|
-
|
|
1145
|
-
## Usage patterns
|
|
1146
|
-
|
|
1147
|
-
### 1) Controlled (most explicit)
|
|
1148
|
-
|
|
1149
|
-
\`\`\`tsx
|
|
1150
|
-
const [createUserResp, createUser, clearCreateUser] = useCreateUser();
|
|
1151
|
-
|
|
1152
|
-
useEffect(() => {
|
|
1153
|
-
createUser(createUserRequest, {});
|
|
1154
|
-
return clearCreateUser; // optional cleanup
|
|
1155
|
-
}, [createUser, clearCreateUser]);
|
|
1156
|
-
\`\`\`
|
|
1157
|
-
|
|
1158
|
-
<details><summary>Description</summary>
|
|
1159
|
-
<p><strong>Use when</strong> you need explicit control over when a request fires, what params/body it uses, and when to clean up. Ideal for search forms, pagination, conditional fetches.</p>
|
|
1160
|
-
</details>
|
|
1161
|
-
|
|
1162
|
-
### 2) Lifecycle-bound (shorthand)
|
|
1163
|
-
|
|
1164
|
-
\`\`\`tsx
|
|
1165
|
-
const [createUserResp] = useCreateUser({
|
|
1166
|
-
fetchOnMount: true,
|
|
1167
|
-
clearOnUnmount: true,
|
|
1168
|
-
body: createUserRequest,
|
|
1169
|
-
params: {},
|
|
1170
|
-
});
|
|
1171
|
-
\`\`\`
|
|
1172
|
-
|
|
1173
|
-
<details><summary>Description</summary>
|
|
1174
|
-
<p><strong>Use when</strong> the data should follow the component lifecycle: fetch once on mount, reset on unmount.</p>
|
|
1175
|
-
</details>
|
|
1176
|
-
|
|
1177
|
-
### 3) Passive observer (render when data arrives)
|
|
1178
|
-
|
|
1179
|
-
\`\`\`tsx
|
|
1180
|
-
const [createUserResp] = useCreateUser();
|
|
1181
|
-
return isSuccess(createUserResp) ? <>{String(createUserResp.data)}</> : null;
|
|
1182
|
-
\`\`\`
|
|
1183
|
-
|
|
1184
|
-
<details><summary>Description</summary>
|
|
1185
|
-
<p><strong>Use when</strong> another part of the app triggers the fetch and this component only reads the state.</p>
|
|
1186
|
-
</details>
|
|
1187
|
-
|
|
1188
|
-
### 4) Keep previous success while refetching (sticky)
|
|
1189
|
-
|
|
1190
|
-
\`\`\`tsx
|
|
1191
|
-
const [createUserData, setCreateUserData] = useState<any>();
|
|
1192
|
-
const [createUserResp, createUser] = useCreateUser();
|
|
1193
|
-
|
|
1194
|
-
useEffect(() => {
|
|
1195
|
-
if (isSuccess(createUserResp)) setCreateUserData(createUserResp.data);
|
|
1196
|
-
}, [createUserResp]);
|
|
1197
|
-
|
|
1198
|
-
return (
|
|
1199
|
-
<>
|
|
1200
|
-
{isPending(createUserResp) && createUserData ? (
|
|
1201
|
-
<div>Refreshing…</div>
|
|
1202
|
-
) : null}
|
|
1203
|
-
<pre>
|
|
1204
|
-
{JSON.stringify(
|
|
1205
|
-
isSuccess(createUserResp) ? createUserResp.data : createUserData,
|
|
1206
|
-
null,
|
|
1207
|
-
2,
|
|
1208
|
-
)}
|
|
1209
|
-
</pre>
|
|
1210
|
-
</>
|
|
1211
|
-
);
|
|
1212
|
-
\`\`\`
|
|
1213
|
-
|
|
1214
|
-
<details><summary>Description</summary>
|
|
1215
|
-
<p><strong>Use when</strong> you want SWR-like UX without flicker.</p>
|
|
1216
|
-
</details>
|
|
1217
|
-
|
|
1218
|
-
### 5) Multiple instances of the same hook (isolate with \`key\`)
|
|
1219
|
-
|
|
1220
|
-
\`\`\`tsx
|
|
1221
|
-
const a = useCreateUser({ key: "A", fetchOnMount: true, params: {} });
|
|
1222
|
-
const b = useCreateUser({ key: "B", fetchOnMount: true, params: {} });
|
|
1223
|
-
\`\`\`
|
|
1224
|
-
|
|
1225
|
-
<details><summary>Description</summary>
|
|
1226
|
-
<p>Use unique keys to prevent state collisions.</p>
|
|
1227
|
-
</details>
|
|
1228
|
-
|
|
1229
|
-
---
|
|
1230
|
-
|
|
1231
|
-
## Before / After mini-migrations
|
|
1232
|
-
|
|
1233
|
-
### If you mistakenly used Stateful for a simple submit → switch to Async
|
|
1234
|
-
|
|
1235
|
-
\`\`\`diff
|
|
1236
|
-
- const [createUserResp, createUser] = useCreateUser();
|
|
1237
|
-
- createUser(createUserRequest, {});
|
|
1238
|
-
+ const [fn] = useCreateUserAsync();
|
|
1239
|
-
+ await fn(createUserRequest);
|
|
1240
|
-
\`\`\`
|
|
1241
|
-
|
|
1242
|
-
### If you started with Async but need to read later in another component → Stateful
|
|
1243
|
-
|
|
1244
|
-
\`\`\`diff
|
|
1245
|
-
- const [fn] = useCreateUserAsync();
|
|
1246
|
-
- const data = await fn(createUserRequest);
|
|
1247
|
-
+ const [createUserResp, createUser] = useCreateUser({ fetchOnMount: true, clearOnUnmount: true, params: {}, body: createUserRequest });
|
|
1248
|
-
+ // read from createUserResp anywhere with useCreateUser()
|
|
1249
|
-
\`\`\`
|
|
1250
|
-
|
|
1251
|
-
---
|
|
1252
|
-
|
|
1253
|
-
## Anti-patterns
|
|
1254
|
-
|
|
1255
|
-
<details><summary>Don’t use Stateful for field validations or a one-off submit</summary>
|
|
1256
|
-
Use the Async variant instead: \`const [fn] = useCreateUserAsync()\`.
|
|
1257
|
-
</details>
|
|
1258
|
-
<details><summary>Don’t use Async for long-lived lists or detail views</summary>
|
|
1259
|
-
Use Stateful so other components can read the same data and you can avoid refetch churn.
|
|
1260
|
-
</details>
|
|
1261
|
-
<details><summary>Don’t forget required \`params\` when using \`fetchOnMount\`</summary>
|
|
1262
|
-
Provide \`params\` (and \`body\` if applicable) or switch to the controlled pattern.
|
|
1263
|
-
</details>
|
|
1264
|
-
<details><summary>Rendering the same hook twice without a \`key\`</summary>
|
|
1265
|
-
If they should be independent, add a unique \`key\`.
|
|
1266
|
-
</details>
|
|
1267
|
-
|
|
1268
|
-
---
|
|
1269
|
-
|
|
1270
|
-
## Error & UX guidance
|
|
1271
|
-
|
|
1272
|
-
- **Loading:** early return or inline spinner. Prefer **sticky data** to avoid blanking content.
|
|
1273
|
-
- **Errors:** show a banner or inline errors depending on UX; keep previous good state if possible.
|
|
1274
|
-
- **Cleanup:** prefer \`clearOnUnmount: true\` as the default.
|
|
1275
|
-
|
|
1276
|
-
---
|
|
1277
|
-
|
|
1278
|
-
## Concurrency patterns
|
|
1279
|
-
|
|
1280
|
-
- **Refresh:** call the action again; combine with sticky data for smooth UX.
|
|
1281
|
-
- **Dedupe:** isolate instances with \`key\`.
|
|
1282
|
-
- **Parallel:** render two keyed instances; don’t share the same key.
|
|
1283
|
-
|
|
1284
|
-
---
|
|
1285
|
-
|
|
1286
|
-
## Full examples
|
|
1287
|
-
|
|
1288
|
-
### Short format (lifecycle-bound)
|
|
1289
|
-
|
|
1290
|
-
\`\`\`tsx
|
|
1291
|
-
import { useCreateUser } from "@intrig/react/users/client";
|
|
1292
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
1293
|
-
import { useMemo } from "react";
|
|
1294
|
-
|
|
1295
|
-
function ShortExample() {
|
|
1296
|
-
const [createUserResp] = useCreateUser({
|
|
1297
|
-
fetchOnMount: true,
|
|
1298
|
-
clearOnUnmount: true,
|
|
1299
|
-
body: createUserRequest,
|
|
1300
|
-
params: {},
|
|
1301
|
-
});
|
|
1302
|
-
|
|
1303
|
-
const createUserData = useMemo(
|
|
1304
|
-
() => (isSuccess(createUserResp) ? createUserResp.data : undefined),
|
|
1305
|
-
[createUserResp],
|
|
1306
|
-
);
|
|
1307
|
-
|
|
1308
|
-
if (isPending(createUserResp)) return <>Loading…</>;
|
|
1309
|
-
if (isError(createUserResp))
|
|
1310
|
-
return <>Error: {String(createUserResp.error)}</>;
|
|
1311
|
-
return <pre>{JSON.stringify(createUserData, null, 2)}</pre>;
|
|
1312
|
-
}
|
|
1313
|
-
\`\`\`
|
|
1314
|
-
|
|
1315
|
-
<details><summary>Description</summary>
|
|
1316
|
-
<p>Compact lifecycle-bound approach. Great for read-only pages that load once and clean up on unmount.</p>
|
|
1317
|
-
</details>
|
|
1318
|
-
|
|
1319
|
-
### Controlled format (explicit actions)
|
|
1320
|
-
|
|
1321
|
-
\`\`\`tsx
|
|
1322
|
-
import { useCreateUser } from "@intrig/react/users/client";
|
|
1323
|
-
import { isPending, isError, isSuccess } from "@intrig/react";
|
|
1324
|
-
import { useEffect, useMemo } from "react";
|
|
1325
|
-
|
|
1326
|
-
function ControlledExample() {
|
|
1327
|
-
const [createUserResp, createUser, clearCreateUser] = useCreateUser();
|
|
1328
|
-
|
|
1329
|
-
useEffect(() => {
|
|
1330
|
-
createUser(createUserRequest, {});
|
|
1331
|
-
return clearCreateUser;
|
|
1332
|
-
}, [createUser, clearCreateUser]);
|
|
1333
|
-
|
|
1334
|
-
const createUserData = useMemo(
|
|
1335
|
-
() => (isSuccess(createUserResp) ? createUserResp.data : undefined),
|
|
1336
|
-
[createUserResp],
|
|
1337
|
-
);
|
|
1338
|
-
|
|
1339
|
-
if (isPending(createUserResp)) return <>Loading…</>;
|
|
1340
|
-
if (isError(createUserResp))
|
|
1341
|
-
return <>An error occurred: {String(createUserResp.error)}</>;
|
|
1342
|
-
return <pre>{JSON.stringify(createUserData, null, 2)}</pre>;
|
|
1343
|
-
}
|
|
1344
|
-
\`\`\`
|
|
1345
|
-
|
|
1346
|
-
---
|
|
1347
|
-
|
|
1348
|
-
## Gotchas & Tips
|
|
1349
|
-
|
|
1350
|
-
- Prefer **\`clearOnUnmount: true\`** in most components.
|
|
1351
|
-
- Use **\`key\`** for multiple independent instances.
|
|
1352
|
-
- Memoize derived values with **\`useMemo\`** to avoid churn.
|
|
1353
|
-
- Inline indicators keep the rest of the page interactive.
|
|
1354
|
-
|
|
1355
|
-
---
|
|
1356
|
-
|
|
1357
|
-
## Reference: State helpers
|
|
1358
|
-
|
|
1359
|
-
\`\`\`ts
|
|
1360
|
-
if (isPending(createUserResp)) {
|
|
1361
|
-
/* show spinner */
|
|
1362
|
-
}
|
|
1363
|
-
if (isError(createUserResp)) {
|
|
1364
|
-
/* show error */
|
|
1365
|
-
}
|
|
1366
|
-
if (isSuccess(createUserResp)) {
|
|
1367
|
-
/* read createUserResp.data */
|
|
1368
|
-
}
|
|
1369
|
-
\`\`\`
|
|
1370
|
-
"
|
|
1371
|
-
`;
|