React Query 是一个强大的状态管理库,专门用于处理数据获取、缓存和更新,尤其适合与 API 交互。它提供了许多高级特性,如自动缓存、离线状态管理、数据过期和重新获取等。
安装:
npm install react-query
导入并配置 React Query:
在你的应用中,你需要导入 useQuery Hook
并设置配置对象。
import { useQuery } from 'react-query';
const queryClient = new QueryClient();
可以通过 QueryClientProvider
将 queryClient
包裹在你的根组件周围,以便在整个应用中使用。
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your application */}
</QueryClientProvider>
);
}
使用 useQuery Hook:
使用 useQuery
来发起 API 请求并管理响应数据。
function MyComponent() {
const { data, status, error } = useQuery('myQueryKey', () => fetch('https://api.example.com/data'));
if (status === 'loading') return 'Loading...';
if (error) return 'An error occurred.';
return <div>{data}</div>;
}
这里,myQueryKey
是查询的唯一标识符,fetch('https://api.example.com/data')
是实际的 API 调用。
配置选项:
useQuery
接受一个配置对象,可以设置缓存策略、重试逻辑等。
const { data } = useQuery(
'myQueryKey',
() => fetch('https://api.example.com/data'),
{
staleTime: 60000, // 数据在多久后被视为过期并重新获取
retry: 3, // 重试次数
refetchOnWindowFocus: false, // 窗口聚焦时是否重新获取数据
}
);
手动操作:
你可以手动触发数据的重新获取、取消或设置为错误状态。
const { refetch, reset, isFetching } = useQuery('myQueryKey', fetchData);
// 重新获取数据
refetch();
// 清除查询状态和数据
reset();
// 检查是否正在获取数据
if (isFetching) console.log('Data is being fetched');
订阅更新:
你可以通过 useQuery
的返回值订阅查询状态变化。
const { status, data, error } = useQuery('myQueryKey', fetchData);
useEffect(() => {
if (status === 'success') console.log('Data updated:', data);
}, [status, data]);
分页:
React Query 支持分页,你可以通过 useInfiniteQuery
Hook 实现无限滚动。
import { useInfiniteQuery } from 'react-query';
function MyInfiniteList() {
const { data, isFetching, hasNextPage, fetchNextPage } = useInfiniteQuery(
'myInfiniteQuery',
async ({ pageParam = 1 }) => {
const response = await fetch(`https://api.example.com/data?page=${pageParam}`);
return response.json();
},
{
getNextPageParam: (lastPage) => lastPage.nextPageToken || false,
}
);
return (
<div>
{data.pages.map((page, index) => (
<ul key={index}>{page.items.map(item => <li key={item.id}>{item.title}</li>)}</ul>
))}
{hasNextPage && !isFetching && (
<button onClick={fetchNextPage}>Load More</button>
)}
</div>
);
}
这里,getNextPageParam
函数用于从上一页的响应中提取下一页的标识符。
缓存更新:
当 API 数据更新时,React Query 可以自动更新缓存中的数据,例如在使用 Mutation
时。
import { useMutation } from 'react-query';
const [updateItem] = useMutation(async (updatedItem) => {
await fetch('https://api.example.com/items/' + updatedItem.id, {
method: 'PUT',
body: JSON.stringify(updatedItem),
});
});
// 更新数据并自动刷新相关查询
updateItem.mutate(updatedItem);
错误处理:
React Query 提供了内置的错误处理机制,你可以通过 error
属性来捕获错误。
const { data, error } = useQuery('myQueryKey', fetchData);
if (error) return <div>Error: {error.message}</div>;
查询缓存清理:
你可以根据需要清理特定查询的缓存。
queryClient.removeQueries('myQueryKey'); // 清理所有匹配的查询
queryClient.cancelQueries('myQueryKey'); // 取消匹配的查询
自定义中间件:
通过自定义中间件,你可以扩展 React Query 的功能,例如添加日志、性能监控等。
import { QueryClient, QueryClientProvider } from 'react-query';
const queryClient = new QueryClient({
queryCache: new QueryCache({
middlewares: [
// 自定义中间件
myCustomMiddleware,
],
}),
});
预取数据(Prefetching):
React Query 允许你在组件渲染之前预取数据,提升用户体验。
import { usePrefetch } from 'react-query';
function MyComponent() {
const prefetchData = usePrefetch('myQueryKey');
useEffect(() => {
// 在组件挂载时预取数据
prefetchData();
}, []);
// ...其他逻辑
}
乐观更新(Optimistic Updates):
在用户触发变更操作时立即更新 UI,然后再等待服务器确认,这能提供即时反馈,提升交互体验。
import { useMutation } from 'react-query';
const [updateTodo, { optimisticData }] = useMutation(updateTodoMutation, {
onMutate: (newTodo) => {
// 乐观地更新客户端缓存
queryClient.setQueryData(['todos', newTodo.id], newTodo);
return { previousTodo: queryClient.getQueryData(['todos', newTodo.id]) };
},
onError: (err, newTodo, context) => {
// 如果发生错误,回滚到之前的值
queryClient.setQueryData(['todos', newTodo.id], context.previousTodo);
},
onSettled: () => {
// 查询已解决时,可以做清理工作
queryClient.invalidateQueries('todos');
},
});
function handleUpdate(todo) {
updateTodo({ ...todo, completed: !todo.completed });
}
并发控制:
有时,你需要控制同时运行的查询数量,特别是当调用多个 API 或有大量并发请求时。
const { data } = useQuery(['concurrentQuery', { limit: 10 }], fetchItems, {
staleTime: Infinity, // 防止数据过期自动重试
refetchInterval: false, // 关闭自动轮询
refetchOnWindowFocus: false, // 不在窗口聚焦时自动重试
retry: false, // 失败时不重试
useErrorBoundary: true, // 错误边界,避免组件树崩溃
concurrency: 2, // 最大并发数
});
依赖注入:
如果你的查询逻辑依赖于外部参数,可以通过 useQueries
来并行执行多个查询,每个查询可以有不同的配置。
import { useQueries } from 'react-query';
function MyComponent({ ids }) {
const queries = useQueries(
ids.map(id => ({
queryKey: ['item', id],
queryFn: () => fetchItem(id),
}))
);
return (
<div>
{queries.map(query => (
<div key={query.queryKey[1]}>
{query.isLoading ? 'Loading...' : query.data?.title}
</div>
))}
</div>
);
}
React Query 的设计考虑到了现代 Web 应用的各种复杂场景,通过上述功能,开发者可以轻松实现数据管理和状态同步,同时保持应用的高性能和良好的用户体验。