@ahoo-wang/fetcher-react 3.9.3 → 3.9.5
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 +200 -19
- package/README.zh-CN.md +200 -19
- package/dist/api/apiHooks.d.ts +78 -0
- package/dist/api/apiHooks.d.ts.map +1 -0
- package/dist/api/createExecuteApiHooks.d.ts +11 -8
- package/dist/api/createExecuteApiHooks.d.ts.map +1 -1
- package/dist/api/createQueryApiHooks.d.ts +97 -0
- package/dist/api/createQueryApiHooks.d.ts.map +1 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/index.es.js +458 -429
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,30 +131,26 @@ The `createExecuteApiHooks` function automatically discovers all function method
|
|
|
131
131
|
|
|
132
132
|
```typescript jsx
|
|
133
133
|
import { createExecuteApiHooks } from '@ahoo-wang/fetcher-react';
|
|
134
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
134
135
|
|
|
135
|
-
// Define your API
|
|
136
|
+
// Define your API service using decorators
|
|
137
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
138
|
+
|
|
139
|
+
@api('/users')
|
|
136
140
|
class UserApi {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
@get('/{id}')
|
|
142
|
+
getUser(@path('id') id: string): Promise<User> {
|
|
143
|
+
throw autoGeneratedError(id);
|
|
140
144
|
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
headers: { 'Content-Type': 'application/json' },
|
|
146
|
-
body: JSON.stringify(data),
|
|
147
|
-
});
|
|
148
|
-
return response.json();
|
|
146
|
+
@post('')
|
|
147
|
+
createUser(@body() data: { name: string; email: string }): Promise<User> {
|
|
148
|
+
throw autoGeneratedError(data);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
headers: { 'Content-Type': 'application/json' },
|
|
155
|
-
body: JSON.stringify(updates),
|
|
156
|
-
});
|
|
157
|
-
return response.json();
|
|
151
|
+
@patch('/{id}')
|
|
152
|
+
updateUser(@path('id') id: string, @body() updates: Partial<User>): Promise<User> {
|
|
153
|
+
throw autoGeneratedError(id, updates);
|
|
158
154
|
}
|
|
159
155
|
}
|
|
160
156
|
|
|
@@ -223,11 +219,19 @@ class ApiError extends Error {
|
|
|
223
219
|
}
|
|
224
220
|
|
|
225
221
|
// Generate hooks with custom error type
|
|
222
|
+
@api('/data')
|
|
223
|
+
class DataApi {
|
|
224
|
+
@get('/{id}')
|
|
225
|
+
getData(@path('id') id: string): Promise<Data> {
|
|
226
|
+
throw autoGeneratedError(id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
226
230
|
const apiHooks = createExecuteApiHooks<
|
|
227
231
|
{ getData: (id: string) => Promise<Data> },
|
|
228
232
|
ApiError
|
|
229
233
|
>({
|
|
230
|
-
api:
|
|
234
|
+
api: new DataApi(),
|
|
231
235
|
errorType: ApiError,
|
|
232
236
|
});
|
|
233
237
|
|
|
@@ -295,6 +299,183 @@ function ApiComponent() {
|
|
|
295
299
|
}
|
|
296
300
|
```
|
|
297
301
|
|
|
302
|
+
#### createQueryApiHooks
|
|
303
|
+
|
|
304
|
+
🚀 **Automatic Type-Safe Query API Hooks Generation** - Generate fully typed React query hooks from API objects with automatic query state management, auto-execution, and advanced execution control.
|
|
305
|
+
|
|
306
|
+
The `createQueryApiHooks` function automatically discovers query methods from an API object and creates corresponding React hooks that extend `useQuery`. Each generated hook provides automatic query parameter management, state management, and supports custom execution callbacks with type-safe query access.
|
|
307
|
+
|
|
308
|
+
**Key Features:**
|
|
309
|
+
|
|
310
|
+
- **Automatic Method Discovery**: Traverses object properties and prototype chains
|
|
311
|
+
- **Type-Safe Query Hooks**: Full TypeScript inference for query parameters and return types
|
|
312
|
+
- **Query State Management**: Built-in `setQuery` and `getQuery` for parameter management
|
|
313
|
+
- **Auto-Execution**: Optional automatic execution when query parameters change
|
|
314
|
+
- **Execution Control**: `onBeforeExecute` callback for query inspection/modification and abort controller access
|
|
315
|
+
- **Custom Error Types**: Support for specifying error types beyond the default `FetcherError`
|
|
316
|
+
|
|
317
|
+
```typescript jsx
|
|
318
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
319
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
320
|
+
|
|
321
|
+
// Define your API service using decorators
|
|
322
|
+
@api('/users')
|
|
323
|
+
class UserApi {
|
|
324
|
+
@get('')
|
|
325
|
+
getUsers(query: UserListQuery, attributes?: Record<string, any>): Promise<User[]> {
|
|
326
|
+
throw autoGeneratedError(query, attributes);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@get('/{id}')
|
|
330
|
+
getUser(query: { id: string }, attributes?: Record<string, any>): Promise<User> {
|
|
331
|
+
throw autoGeneratedError(query, attributes);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@post('')
|
|
335
|
+
createUser(query: { name: string; email: string }, attributes?: Record<string, any>): Promise<User> {
|
|
336
|
+
throw autoGeneratedError(query, attributes);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const apiHooks = createQueryApiHooks({ api: new UserApi() });
|
|
341
|
+
|
|
342
|
+
function UserListComponent() {
|
|
343
|
+
const { loading, result, error, execute, setQuery, getQuery } = apiHooks.useGetUsers({
|
|
344
|
+
initialQuery: { page: 1, limit: 10 },
|
|
345
|
+
autoExecute: true,
|
|
346
|
+
onBeforeExecute: (abortController, query) => {
|
|
347
|
+
// query is fully typed as UserListQuery
|
|
348
|
+
console.log('Executing query:', query);
|
|
349
|
+
// Modify query parameters in place if needed
|
|
350
|
+
query.page = Math.max(1, query.page);
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const handlePageChange = (page: number) => {
|
|
355
|
+
// Automatically updates query and triggers execution (if autoExecute: true)
|
|
356
|
+
setQuery({ ...getQuery(), page });
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
if (loading) return <div>Loading...</div>;
|
|
360
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
361
|
+
|
|
362
|
+
return (
|
|
363
|
+
<div>
|
|
364
|
+
<button onClick={() => handlePageChange(2)}>Go to page 2</button>
|
|
365
|
+
{result?.map(user => (
|
|
366
|
+
<div key={user.id}>{user.name}</div>
|
|
367
|
+
))}
|
|
368
|
+
</div>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function UserDetailComponent() {
|
|
373
|
+
const { result: user, execute } = apiHooks.useGetUser({
|
|
374
|
+
initialQuery: { id: '123' },
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<div>
|
|
379
|
+
<button onClick={execute}>Load User</button>
|
|
380
|
+
{user && <div>User: {user.name}</div>}
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Custom Error Types:**
|
|
387
|
+
|
|
388
|
+
```typescript jsx
|
|
389
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
390
|
+
|
|
391
|
+
// Define custom error type
|
|
392
|
+
class ApiError extends Error {
|
|
393
|
+
constructor(
|
|
394
|
+
public statusCode: number,
|
|
395
|
+
message: string,
|
|
396
|
+
) {
|
|
397
|
+
super(message);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Generate query hooks with custom error type
|
|
402
|
+
@api('/data')
|
|
403
|
+
class DataApi {
|
|
404
|
+
@get('/{id}')
|
|
405
|
+
getData(
|
|
406
|
+
query: { id: string },
|
|
407
|
+
attributes?: Record<string, any>,
|
|
408
|
+
): Promise<Data> {
|
|
409
|
+
throw autoGeneratedError(query, attributes);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const apiHooks = createQueryApiHooks<
|
|
414
|
+
{
|
|
415
|
+
getData: (
|
|
416
|
+
query: { id: string },
|
|
417
|
+
attributes?: Record<string, any>,
|
|
418
|
+
) => Promise<Data>;
|
|
419
|
+
},
|
|
420
|
+
ApiError
|
|
421
|
+
>({
|
|
422
|
+
api: new DataApi(),
|
|
423
|
+
errorType: ApiError,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
function MyComponent() {
|
|
427
|
+
const { error, execute } = apiHooks.useGetData();
|
|
428
|
+
|
|
429
|
+
// error is now typed as ApiError | undefined
|
|
430
|
+
if (error) {
|
|
431
|
+
console.log('Status code:', error.statusCode); // TypeScript knows about statusCode
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Advanced Usage with Manual Query Management:**
|
|
437
|
+
|
|
438
|
+
```typescript jsx
|
|
439
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
440
|
+
|
|
441
|
+
const apiHooks = createQueryApiHooks({ api: userApi });
|
|
442
|
+
|
|
443
|
+
function SearchComponent() {
|
|
444
|
+
const { loading, result, setQuery, getQuery } = apiHooks.useGetUsers({
|
|
445
|
+
initialQuery: { search: '', page: 1 },
|
|
446
|
+
autoExecute: false, // Manual execution control
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const handleSearch = (searchTerm: string) => {
|
|
450
|
+
// Update query without automatic execution
|
|
451
|
+
setQuery({ search: searchTerm, page: 1 });
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const handleSearchSubmit = () => {
|
|
455
|
+
// Manually execute with current query
|
|
456
|
+
apiHooks.useGetUsers().execute();
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const currentQuery = getQuery(); // Access current query parameters
|
|
460
|
+
|
|
461
|
+
return (
|
|
462
|
+
<div>
|
|
463
|
+
<input
|
|
464
|
+
value={currentQuery.search}
|
|
465
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
466
|
+
placeholder="Search users..."
|
|
467
|
+
/>
|
|
468
|
+
<button onClick={handleSearchSubmit} disabled={loading}>
|
|
469
|
+
{loading ? 'Searching...' : 'Search'}
|
|
470
|
+
</button>
|
|
471
|
+
{result?.map(user => (
|
|
472
|
+
<div key={user.id}>{user.name}</div>
|
|
473
|
+
))}
|
|
474
|
+
</div>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
298
479
|
### Core Hooks
|
|
299
480
|
|
|
300
481
|
#### useExecutePromise Hook
|
package/README.zh-CN.md
CHANGED
|
@@ -129,30 +129,26 @@ function App() {
|
|
|
129
129
|
|
|
130
130
|
```typescript jsx
|
|
131
131
|
import { createExecuteApiHooks } from '@ahoo-wang/fetcher-react';
|
|
132
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
132
133
|
|
|
133
|
-
//
|
|
134
|
+
// 使用装饰器定义您的 API 服务
|
|
135
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
136
|
+
|
|
137
|
+
@api('/users')
|
|
134
138
|
class UserApi {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
@get('/{id}')
|
|
140
|
+
getUser(@path('id') id: string): Promise<User> {
|
|
141
|
+
throw autoGeneratedError(id);
|
|
138
142
|
}
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
headers: { 'Content-Type': 'application/json' },
|
|
144
|
-
body: JSON.stringify(data),
|
|
145
|
-
});
|
|
146
|
-
return response.json();
|
|
144
|
+
@post('')
|
|
145
|
+
createUser(@body() data: { name: string; email: string }): Promise<User> {
|
|
146
|
+
throw autoGeneratedError(data);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
headers: { 'Content-Type': 'application/json' },
|
|
153
|
-
body: JSON.stringify(updates),
|
|
154
|
-
});
|
|
155
|
-
return response.json();
|
|
149
|
+
@patch('/{id}')
|
|
150
|
+
updateUser(@path('id') id: string, @body() updates: Partial<User>): Promise<User> {
|
|
151
|
+
throw autoGeneratedError(id, updates);
|
|
156
152
|
}
|
|
157
153
|
}
|
|
158
154
|
|
|
@@ -221,11 +217,19 @@ class ApiError extends Error {
|
|
|
221
217
|
}
|
|
222
218
|
|
|
223
219
|
// 使用自定义错误类型生成 hooks
|
|
220
|
+
@api('/data')
|
|
221
|
+
class DataApi {
|
|
222
|
+
@get('/{id}')
|
|
223
|
+
getData(@path('id') id: string): Promise<Data> {
|
|
224
|
+
throw autoGeneratedError(id);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
224
228
|
const apiHooks = createExecuteApiHooks<
|
|
225
229
|
{ getData: (id: string) => Promise<Data> },
|
|
226
230
|
ApiError
|
|
227
231
|
>({
|
|
228
|
-
api:
|
|
232
|
+
api: new DataApi(),
|
|
229
233
|
errorType: ApiError,
|
|
230
234
|
});
|
|
231
235
|
|
|
@@ -293,6 +297,183 @@ function ApiComponent() {
|
|
|
293
297
|
}
|
|
294
298
|
```
|
|
295
299
|
|
|
300
|
+
#### createQueryApiHooks
|
|
301
|
+
|
|
302
|
+
🚀 **自动类型安全查询 API Hooks 生成** - 从 API 对象自动生成完全类型化的 React 查询 hooks,具有自动查询状态管理、自动执行和高级执行控制。
|
|
303
|
+
|
|
304
|
+
`createQueryApiHooks` 函数自动发现 API 对象中的查询方法,并创建相应的扩展 `useQuery` 的 React hooks。每个生成的 hook 都提供自动查询参数管理、状态管理和对具有类型安全查询访问的自定义执行回调的支持。
|
|
305
|
+
|
|
306
|
+
**主要特性:**
|
|
307
|
+
|
|
308
|
+
- **自动方法发现**:遍历对象属性和原型链
|
|
309
|
+
- **类型安全查询 Hooks**:查询参数和返回类型的完整 TypeScript 推断
|
|
310
|
+
- **查询状态管理**:内置 `setQuery` 和 `getQuery` 进行参数管理
|
|
311
|
+
- **自动执行**:查询参数更改时可选的自动执行
|
|
312
|
+
- **执行控制**:`onBeforeExecute` 回调用于查询检查/修改和中止控制器访问
|
|
313
|
+
- **自定义错误类型**:支持指定超出默认 `FetcherError` 的错误类型
|
|
314
|
+
|
|
315
|
+
```typescript jsx
|
|
316
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
317
|
+
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
|
|
318
|
+
|
|
319
|
+
// 使用装饰器定义您的 API 服务
|
|
320
|
+
@api('/users')
|
|
321
|
+
class UserApi {
|
|
322
|
+
@get('')
|
|
323
|
+
getUsers(query: UserListQuery, attributes?: Record<string, any>): Promise<User[]> {
|
|
324
|
+
throw autoGeneratedError(query, attributes);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@get('/{id}')
|
|
328
|
+
getUser(query: { id: string }, attributes?: Record<string, any>): Promise<User> {
|
|
329
|
+
throw autoGeneratedError(query, attributes);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@post('')
|
|
333
|
+
createUser(query: { name: string; email: string }, attributes?: Record<string, any>): Promise<User> {
|
|
334
|
+
throw autoGeneratedError(query, attributes);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const apiHooks = createQueryApiHooks({ api: new UserApi() });
|
|
339
|
+
|
|
340
|
+
function UserListComponent() {
|
|
341
|
+
const { loading, result, error, execute, setQuery, getQuery } = apiHooks.useGetUsers({
|
|
342
|
+
initialQuery: { page: 1, limit: 10 },
|
|
343
|
+
autoExecute: true,
|
|
344
|
+
onBeforeExecute: (abortController, query) => {
|
|
345
|
+
// query 是完全类型化的 UserListQuery
|
|
346
|
+
console.log('正在执行查询:', query);
|
|
347
|
+
// 如果需要,可以就地修改查询参数
|
|
348
|
+
query.page = Math.max(1, query.page);
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const handlePageChange = (page: number) => {
|
|
353
|
+
// 自动更新查询并触发执行(如果 autoExecute: true)
|
|
354
|
+
setQuery({ ...getQuery(), page });
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
if (loading) return <div>正在加载...</div>;
|
|
358
|
+
if (error) return <div>错误: {error.message}</div>;
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<div>
|
|
362
|
+
<button onClick={() => handlePageChange(2)}>转到第2页</button>
|
|
363
|
+
{result?.map(user => (
|
|
364
|
+
<div key={user.id}>{user.name}</div>
|
|
365
|
+
))}
|
|
366
|
+
</div>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function UserDetailComponent() {
|
|
371
|
+
const { result: user, execute } = apiHooks.useGetUser({
|
|
372
|
+
initialQuery: { id: '123' },
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<div>
|
|
377
|
+
<button onClick={execute}>加载用户</button>
|
|
378
|
+
{user && <div>用户: {user.name}</div>}
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**自定义错误类型:**
|
|
385
|
+
|
|
386
|
+
```typescript jsx
|
|
387
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
388
|
+
|
|
389
|
+
// 定义自定义错误类型
|
|
390
|
+
class ApiError extends Error {
|
|
391
|
+
constructor(
|
|
392
|
+
public statusCode: number,
|
|
393
|
+
message: string,
|
|
394
|
+
) {
|
|
395
|
+
super(message);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// 使用自定义错误类型生成查询 hooks
|
|
400
|
+
@api('/data')
|
|
401
|
+
class DataApi {
|
|
402
|
+
@get('/{id}')
|
|
403
|
+
getData(
|
|
404
|
+
query: { id: string },
|
|
405
|
+
attributes?: Record<string, any>,
|
|
406
|
+
): Promise<Data> {
|
|
407
|
+
throw autoGeneratedError(query, attributes);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const apiHooks = createQueryApiHooks<
|
|
412
|
+
{
|
|
413
|
+
getData: (
|
|
414
|
+
query: { id: string },
|
|
415
|
+
attributes?: Record<string, any>,
|
|
416
|
+
) => Promise<Data>;
|
|
417
|
+
},
|
|
418
|
+
ApiError
|
|
419
|
+
>({
|
|
420
|
+
api: new DataApi(),
|
|
421
|
+
errorType: ApiError,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
function MyComponent() {
|
|
425
|
+
const { error, execute } = apiHooks.useGetData();
|
|
426
|
+
|
|
427
|
+
// error 现在类型化为 ApiError | undefined
|
|
428
|
+
if (error) {
|
|
429
|
+
console.log('状态码:', error.statusCode); // TypeScript 知道 statusCode
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**高级用法与手动查询管理:**
|
|
435
|
+
|
|
436
|
+
```typescript jsx
|
|
437
|
+
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
|
|
438
|
+
|
|
439
|
+
const apiHooks = createQueryApiHooks({ api: userApi });
|
|
440
|
+
|
|
441
|
+
function SearchComponent() {
|
|
442
|
+
const { loading, result, setQuery, getQuery } = apiHooks.useGetUsers({
|
|
443
|
+
initialQuery: { search: '', page: 1 },
|
|
444
|
+
autoExecute: false, // 手动执行控制
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const handleSearch = (searchTerm: string) => {
|
|
448
|
+
// 更新查询而不自动执行
|
|
449
|
+
setQuery({ search: searchTerm, page: 1 });
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const handleSearchSubmit = () => {
|
|
453
|
+
// 使用当前查询手动执行
|
|
454
|
+
apiHooks.useGetUsers().execute();
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const currentQuery = getQuery(); // 访问当前查询参数
|
|
458
|
+
|
|
459
|
+
return (
|
|
460
|
+
<div>
|
|
461
|
+
<input
|
|
462
|
+
value={currentQuery.search}
|
|
463
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
464
|
+
placeholder="搜索用户..."
|
|
465
|
+
/>
|
|
466
|
+
<button onClick={handleSearchSubmit} disabled={loading}>
|
|
467
|
+
{loading ? '搜索中...' : '搜索'}
|
|
468
|
+
</button>
|
|
469
|
+
{result?.map(user => (
|
|
470
|
+
<div key={user.id}>{user.name}</div>
|
|
471
|
+
))}
|
|
472
|
+
</div>
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
296
477
|
### 核心 Hooks
|
|
297
478
|
|
|
298
479
|
#### useExecutePromise
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities and types for API hooks generation.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Converts a method name to a React hook name by prefixing with 'use' and capitalizing the first letter.
|
|
6
|
+
* @param methodName - The method name to convert.
|
|
7
|
+
* @returns The converted hook name.
|
|
8
|
+
* @throws {Error} If methodName is empty or null.
|
|
9
|
+
* @example
|
|
10
|
+
* methodNameToHookName('getUser') // 'useGetUser'
|
|
11
|
+
* methodNameToHookName('createPost') // 'useCreatePost'
|
|
12
|
+
* methodNameToHookName('APIGetData') // 'useAPIGetData'
|
|
13
|
+
*/
|
|
14
|
+
export declare function methodNameToHookName(methodName: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Collects all function methods from an object and its prototype chain.
|
|
17
|
+
* @param obj - The object to collect methods from.
|
|
18
|
+
* @returns A map of method names to bound methods.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectMethods<T extends (...args: any[]) => Promise<any>>(obj: Record<string, any>): Map<string, T>;
|
|
21
|
+
/**
|
|
22
|
+
* Common configuration options for API hooks creation functions.
|
|
23
|
+
* @template API - The API object type.
|
|
24
|
+
*/
|
|
25
|
+
export interface CreateApiHooksOptions<API extends Record<string, any>> {
|
|
26
|
+
/**
|
|
27
|
+
* The API object containing methods to be wrapped into hooks.
|
|
28
|
+
*/
|
|
29
|
+
api: API;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Utility type to extract the hook name from a method name.
|
|
33
|
+
* @template K - The method name key.
|
|
34
|
+
*/
|
|
35
|
+
export type HookName<K extends string> = `use${Capitalize<K>}`;
|
|
36
|
+
/**
|
|
37
|
+
* Utility type to check if a value is a function that returns a Promise.
|
|
38
|
+
* @template T - The value to check.
|
|
39
|
+
*/
|
|
40
|
+
export type IsPromiseFunction<T> = T extends (...args: any[]) => Promise<any> ? true : false;
|
|
41
|
+
/**
|
|
42
|
+
* Utility type to extract the parameters of a promise-returning function.
|
|
43
|
+
* @template T - The function type.
|
|
44
|
+
*/
|
|
45
|
+
export type FunctionParameters<T> = T extends (...args: infer P) => Promise<any> ? P : never;
|
|
46
|
+
/**
|
|
47
|
+
* Utility type to extract the resolved return type of a promise-returning function.
|
|
48
|
+
* @template T - The function type.
|
|
49
|
+
*/
|
|
50
|
+
export type FunctionReturnType<T> = T extends (...args: any[]) => Promise<infer R> ? Awaited<R> : never;
|
|
51
|
+
/**
|
|
52
|
+
* Utility type for API method signatures.
|
|
53
|
+
* @template TArgs - Parameter types.
|
|
54
|
+
* @template TReturn - Return type.
|
|
55
|
+
*/
|
|
56
|
+
export type ApiMethod<TArgs extends any[] = any[], TReturn = any> = (...args: TArgs) => Promise<TReturn>;
|
|
57
|
+
/**
|
|
58
|
+
* Utility type for query method signatures (with query, attributes, abortController).
|
|
59
|
+
* @template Q - Query type.
|
|
60
|
+
* @template TReturn - Return type.
|
|
61
|
+
*/
|
|
62
|
+
export type QueryMethod<Q = any, TReturn = any> = (query: Q, attributes?: Record<string, any>, abortController?: AbortController) => Promise<TReturn>;
|
|
63
|
+
/**
|
|
64
|
+
* Common callback type for pre-execution hooks.
|
|
65
|
+
* @template TParams - The parameters type passed to the callback.
|
|
66
|
+
*/
|
|
67
|
+
export type OnBeforeExecuteCallback<TParams> = (abortController: AbortController | undefined, params: TParams) => void;
|
|
68
|
+
/**
|
|
69
|
+
* Utility type to create API hooks mapping.
|
|
70
|
+
* @template API - The API object type.
|
|
71
|
+
* @template MethodType - The method signature type.
|
|
72
|
+
* @template HookType - The hook function type.
|
|
73
|
+
* @template E - Error type.
|
|
74
|
+
*/
|
|
75
|
+
export type ApiHooksMapping<API extends Record<string, any>, MethodType extends (...args: any[]) => Promise<any>, HookType> = {
|
|
76
|
+
[K in keyof API as API[K] extends MethodType ? HookName<string & K> : never]: API[K] extends MethodType ? HookType : never;
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=apiHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiHooks.d.ts","sourceRoot":"","sources":["../../src/api/apiHooks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EACvE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACvB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CA6BhB;AAMD;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACpE;;OAEG;IACH,GAAG,EAAE,GAAG,CAAC;CACV;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;AAE/D;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,GACzE,IAAI,GACJ,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,GAC5E,CAAC,GACD,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAC5C,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,OAAO,CAAC,MAAM,CAAC,CAAC,GACjB,OAAO,CAAC,CAAC,CAAC,GACV,KAAK,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,OAAO,GAAG,GAAG,IAAI,CAClE,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAChD,KAAK,EAAE,CAAC,EACR,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,eAAe,CAAC,EAAE,eAAe,KAC9B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;GAGG;AACH,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAAI,CAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,MAAM,EAAE,OAAO,KACZ,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,CACzB,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,UAAU,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EACnD,QAAQ,IACN;KACD,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,UAAU,GACxC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GACpB,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,UAAU,GAAG,QAAQ,GAAG,KAAK;CACzD,CAAC"}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { UseExecutePromiseReturn, UseExecutePromiseOptions } from '../core';
|
|
2
2
|
import { FetcherError } from '@ahoo-wang/fetcher';
|
|
3
|
+
import { CreateApiHooksOptions, HookName, ApiMethod, FunctionParameters, FunctionReturnType, OnBeforeExecuteCallback } from './apiHooks';
|
|
3
4
|
/**
|
|
4
5
|
* Configuration options for createExecuteApiHooks.
|
|
5
6
|
* @template API - The API object type containing methods that return promises.
|
|
6
7
|
*/
|
|
7
|
-
export interface CreateExecuteApiHooksOptions<API extends Record<string, any>> {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export interface CreateExecuteApiHooksOptions<API extends Record<string, any>> extends CreateApiHooksOptions<API> {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Configuration options for createQueryApiHooks.
|
|
12
|
+
* @template API - The API object type containing query methods.
|
|
13
|
+
*/
|
|
14
|
+
export interface CreateQueryApiHooksOptions<API extends Record<string, any>> extends CreateApiHooksOptions<API> {
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
17
|
* Options for useApiMethodExecute hook.
|
|
@@ -34,7 +37,7 @@ export interface UseApiMethodExecuteOptions<TArgs = any[], TData = any, E = Fetc
|
|
|
34
37
|
* }
|
|
35
38
|
* }
|
|
36
39
|
*/
|
|
37
|
-
onBeforeExecute?:
|
|
40
|
+
onBeforeExecute?: OnBeforeExecuteCallback<TArgs>;
|
|
38
41
|
}
|
|
39
42
|
/**
|
|
40
43
|
* The return type of createExecuteApiHooks.
|
|
@@ -45,8 +48,8 @@ export interface UseApiMethodExecuteOptions<TArgs = any[], TData = any, E = Fetc
|
|
|
45
48
|
* @template E - The error type for all hooks (defaults to FetcherError).
|
|
46
49
|
*/
|
|
47
50
|
export type APIHooks<API extends Record<string, any>, E = FetcherError> = {
|
|
48
|
-
[K in keyof API as API[K] extends
|
|
49
|
-
execute: (...params:
|
|
51
|
+
[K in keyof API as API[K] extends ApiMethod ? HookName<string & K> : never]: API[K] extends ApiMethod ? (options?: UseApiMethodExecuteOptions<FunctionParameters<API[K]>, FunctionReturnType<API[K]>, E>) => UseExecutePromiseReturn<FunctionReturnType<API[K]>, E> & {
|
|
52
|
+
execute: (...params: FunctionParameters<API[K]>) => Promise<void>;
|
|
50
53
|
} : never;
|
|
51
54
|
};
|
|
52
55
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExecuteApiHooks.d.ts","sourceRoot":"","sources":["../../src/api/createExecuteApiHooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,uBAAuB,EACvB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"createExecuteApiHooks.d.ts","sourceRoot":"","sources":["../../src/api/createExecuteApiHooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGL,qBAAqB,EACrB,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,WAAW,4BAA4B,CAC3C,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC/B,SAAQ,qBAAqB,CAAC,GAAG,CAAC;CAAG;AAEvC;;;GAGG;AACH,MAAM,WAAW,0BAA0B,CACzC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC/B,SAAQ,qBAAqB,CAAC,GAAG,CAAC;CAAG;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CACzC,KAAK,GAAG,GAAG,EAAE,EACb,KAAK,GAAG,GAAG,EACX,CAAC,GAAG,YAAY,CAChB,SAAQ,wBAAwB,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1C;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,EAAE,uBAAuB,CAAC,KAAK,CAAC,CAAC;CAClD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,IAAI;KACvE,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS,GACvC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GACpB,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS,GAChC,CACE,OAAO,CAAC,EAAE,0BAA0B,CAClC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAC1B,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAC1B,CAAC,CACF,KACE,uBAAuB,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAC5D,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACnE,GACD,KAAK;CACV,CAAC;AA4DF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,CAAC,GAAG,YAAY,EAChB,OAAO,EAAE,4BAA4B,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAa9D"}
|