这种模式同时适用于React与React Native。
React Query的数据获取能力很强,但如果在多个页面或组件中反复编写相同查询,代码就会冗长,维护也会更困难。把查询逻辑拆分为自定义Hook,可以同时提升复用性和可读性。
本文介绍如何
通过自定义Hook封装React Query
,更高效地使用它。我们以JSONPlaceholder API为例,展示可以直接用于实际项目的结构。
首先回顾直接使用useQuery的基本结构。
components/UserList.tsxconst fetchUsers = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/users');
return res.data;
};
function UserList() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생!</div>;
return <div>{data.map((user) => user.name)}</div>;
}
这种代码只在一两个组件中使用时没有太大问题,但如果多个地方都采用相同写法,就会产生大量重复。
现在把这个模式封装为自定义Hook。例如,可以创建useUsers.ts,按以下方式组织。
hooks/useUsers.tsimport { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchUsers = async () => {
const res = await axios.get('https://jsonplaceholder.typicode.com/users');
return res.data;
};
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
}
之后,组件中就可以这样使用。
components/UserList.tsximport { useUsers } from '../hooks/useUsers';
export function UserList() {
const { data, isLoading, error } = useUsers();
if (isLoading) return <div>로딩 중...</div>;
if (error) return <div>에러 발생!</div>;
return <div>{data.map((user) => user.name)}</div>;
}
下面是获取特定用户文章的useUserPosts(userId)形式的Hook。
hooks/useUserPosts.tsexport function useUserPosts(userId: number) {
return useQuery({
queryKey: ['user', userId, 'posts'],
queryFn: async () => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}/posts`);
return res.data;
},
enabled: !!userId,
});
}
enabled: !!userId会阻止查询在userId尚未定义时执行。
如果希望调用Hook时可以灵活传递配置,可以把options作为参数接收。
hooks/useUsers.tsexport function useUsers(options?: UseQueryOptions) {
return useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
...options,
});
}
这样,调用方就可以自由设置staleTime、onSuccess、onError等选项,使用起来更加灵活。
通过定义fetch返回结果的类型,可以保证类型安全。
hooks/useUserPosts.tsinterface FetchUserPostsResponse {
id: number;
title: string;
body: string;
}
export function useUserPosts(userId: number, options?: UseQueryOptions<FetchUserPostsResponse, Error>) {
return useQuery({
queryKey: ['user', userId, 'posts'],
queryFn: async () => {
const res = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}/posts`);
return res.data;
},
...options,
});
}
React Query提供了强大的功能,但如果不封装为自定义Hook,就容易产生重复代码。把重复的
queryKey、
queryFn等提取到Hook中,可以在多个页面高效复用,也便于测试和维护。
设计良好的自定义Hook,还可以提升整个团队的代码质量与一致性。今后使用React Query时,不妨优先采用
基于Hook的结构
。