v3.9.5
What's Changed
🎉 New Features
- feat(react-api): add query API hooks and utilities for React by @Ahoo-Wang in #987
createQueryApiHooks
🚀 自动类型安全查询 API Hooks 生成 - 从 API 对象自动生成完全类型化的 React 查询 hooks,具有自动查询状态管理、自动执行和高级执行控制。
createQueryApiHooks 函数自动发现 API 对象中的查询方法,并创建相应的扩展 useQuery 的 React hooks。每个生成的 hook 都提供自动查询参数管理、状态管理和对具有类型安全查询访问的自定义执行回调的支持。
主要特性:
- 自动方法发现:遍历对象属性和原型链
- 类型安全查询 Hooks:查询参数和返回类型的完整 TypeScript 推断
- 查询状态管理:内置
setQuery和getQuery进行参数管理 - 自动执行:查询参数更改时可选的自动执行
- 执行控制:
onBeforeExecute回调用于查询检查/修改和中止控制器访问 - 自定义错误类型:支持指定超出默认
FetcherError的错误类型
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
// 使用装饰器定义您的 API 服务
@api('/users')
class UserApi {
@get('')
getUsers(query: UserListQuery, attributes?: Record<string, any>): Promise<User[]> {
throw autoGeneratedError(query, attributes);
}
@get('/{id}')
getUser(query: { id: string }, attributes?: Record<string, any>): Promise<User> {
throw autoGeneratedError(query, attributes);
}
@post('')
createUser(query: { name: string; email: string }, attributes?: Record<string, any>): Promise<User> {
throw autoGeneratedError(query, attributes);
}
}
const apiHooks = createQueryApiHooks({ api: new UserApi() });
function UserListComponent() {
const { loading, result, error, execute, setQuery, getQuery } = apiHooks.useGetUsers({
initialQuery: { page: 1, limit: 10 },
autoExecute: true,
onBeforeExecute: (abortController, query) => {
// query 是完全类型化的 UserListQuery
console.log('正在执行查询:', query);
// 如果需要,可以就地修改查询参数
query.page = Math.max(1, query.page);
},
});
const handlePageChange = (page: number) => {
// 自动更新查询并触发执行(如果 autoExecute: true)
setQuery({ ...getQuery(), page });
};
if (loading) return <div>正在加载...</div>;
if (error) return <div>错误: {error.message}</div>;
return (
<div>
<button onClick={() => handlePageChange(2)}>转到第2页</button>
{result?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
function UserDetailComponent() {
const { result: user, execute } = apiHooks.useGetUser({
initialQuery: { id: '123' },
});
return (
<div>
<button onClick={execute}>加载用户</button>
{user && <div>用户: {user.name}</div>}
</div>
);
}自定义错误类型:
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
// 定义自定义错误类型
class ApiError extends Error {
constructor(
public statusCode: number,
message: string,
) {
super(message);
}
}
// 使用自定义错误类型生成查询 hooks
@api('/data')
class DataApi {
@get('/{id}')
getData(
query: { id: string },
attributes?: Record<string, any>,
): Promise<Data> {
throw autoGeneratedError(query, attributes);
}
}
const apiHooks = createQueryApiHooks<
{
getData: (
query: { id: string },
attributes?: Record<string, any>,
) => Promise<Data>;
},
ApiError
>({
api: new DataApi(),
errorType: ApiError,
});
function MyComponent() {
const { error, execute } = apiHooks.useGetData();
// error 现在类型化为 ApiError | undefined
if (error) {
console.log('状态码:', error.statusCode); // TypeScript 知道 statusCode
}
}高级用法与手动查询管理:
import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
const apiHooks = createQueryApiHooks({ api: userApi });
function SearchComponent() {
const { loading, result, setQuery, getQuery } = apiHooks.useGetUsers({
initialQuery: { search: '', page: 1 },
autoExecute: false, // 手动执行控制
});
const handleSearch = (searchTerm: string) => {
// 更新查询而不自动执行
setQuery({ search: searchTerm, page: 1 });
};
const handleSearchSubmit = () => {
// 使用当前查询手动执行
apiHooks.useGetUsers().execute();
};
const currentQuery = getQuery(); // 访问当前查询参数
return (
<div>
<input
value={currentQuery.search}
onChange={(e) => handleSearch(e.target.value)}
placeholder="搜索用户..."
/>
<button onClick={handleSearchSubmit} disabled={loading}>
{loading ? '搜索中...' : '搜索'}
</button>
{result?.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}Full Changelog: v3.9.3...v3.9.5