Loading...
Loading...
Compare original and translation side by side
npm install @tanstack/react-querynpm install @tanstack/react-queryundefinedundefinedQueryClientProviderimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}QueryClientProviderimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}import { useQuery } from '@tanstack/react-query';
function TodoList() {
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: async () => {
const res = await fetch('https://api.example.com/todos');
if (!res.ok) throw new Error('Network response was not ok');
return res.json();
},
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}import { useQuery } from '@tanstack/react-query';
function TodoList() {
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: async () => {
const res = await fetch('https://api.example.com/todos');
if (!res.ok) throw new Error('Network response was not ok');
return res.json();
},
});
if (isLoading) return <div>加载中...</div>;
if (error) return <div>错误:{error.message}</div>;
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateTodo() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newTodo) => {
const res = await fetch('https://api.example.com/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
headers: { 'Content-Type': 'application/json' },
});
return res.json();
},
onSuccess: () => {
// Invalidate and refetch todos
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
return (
<button onClick={() => mutation.mutate({ title: 'New Todo' })}>
{mutation.isPending ? 'Creating...' : 'Create Todo'}
</button>
);
}import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateTodo() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: async (newTodo) => {
const res = await fetch('https://api.example.com/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
headers: { 'Content-Type': 'application/json' },
});
return res.json();
},
onSuccess: () => {
// 失效并重新获取todos数据
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
return (
<button onClick={() => mutation.mutate({ title: 'New Todo' })}>
{mutation.isPending ? '创建中...' : '创建待办事项'}
</button>
);
}// Simple key
useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
// Key with variables
useQuery({ queryKey: ['todo', todoId], queryFn: () => fetchTodo(todoId) });
// Hierarchical keys
useQuery({ queryKey: ['todos', 'list', { filters, page }], queryFn: fetchTodos });['todos']['todos', { page: 1 }]{ queryKey: ['todos'] }// 简单键
useQuery({ queryKey: ['todos'], queryFn: fetchTodos });
// 带变量的键
useQuery({ queryKey: ['todo', todoId], queryFn: () => fetchTodo(todoId) });
// 层级键
useQuery({ queryKey: ['todos', 'list', { filters, page }], queryFn: fetchTodos });['todos']['todos', { page: 1 }]{ queryKey: ['todos'] }// Using fetch
queryFn: async () => {
const res = await fetch(url);
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
// Using axios
queryFn: () => axios.get(url).then(res => res.data)
// With query key access
queryFn: ({ queryKey }) => {
const [_, todoId] = queryKey;
return fetchTodo(todoId);
}// 使用fetch
queryFn: async () => {
const res = await fetch(url);
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
// 使用axios
queryFn: () => axios.get(url).then(res => res.data)
// 访问查询键
queryFn: ({ queryKey }) => {
const [_, todoId] = queryKey;
return fetchTodo(todoId);
}// Override defaults globally
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 10, // 10 minutes
},
},
});
// Or per query
useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
staleTime: 1000 * 60, // 1 minute
retry: 5,
});// 全局覆盖默认配置
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5分钟
gcTime: 1000 * 60 * 10, // 10分钟
},
},
});
// 或针对单个查询配置
useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
staleTime: 1000 * 60, // 1分钟
retry: 5,
});pendingerrorsuccessfetchingpausedidleconst { data, status, fetchStatus, isLoading, isFetching } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
});
// isLoading = status === 'pending'
// isFetching = fetchStatus === 'fetching'pendingerrorsuccessfetchingpausedidleconst { data, status, fetchStatus, isLoading, isFetching } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
});
// isLoading = status === 'pending'
// isFetching = fetchStatus === 'fetching'const queryClient = useQueryClient();
// Invalidate all todos queries
queryClient.invalidateQueries({ queryKey: ['todos'] });
// Invalidate specific query
queryClient.invalidateQueries({ queryKey: ['todo', todoId] });
// Invalidate and refetch immediately
queryClient.invalidateQueries({
queryKey: ['todos'],
refetchType: 'active' // only refetch active queries
});const queryClient = useQueryClient();
// 使所有todos查询失效
queryClient.invalidateQueries({ queryKey: ['todos'] });
// 使特定查询失效
queryClient.invalidateQueries({ queryKey: ['todo', todoId] });
// 使查询失效并立即重新获取
queryClient.invalidateQueries({
queryKey: ['todos'],
refetchType: 'active' // 仅重新获取活跃的查询
});const mutation = useMutation({
mutationFn: (newTodo) => {
return fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
});
},
onSuccess: (data, variables, context) => {
console.log('Success!', data);
},
onError: (error, variables, context) => {
console.error('Error:', error);
},
onSettled: (data, error, variables, context) => {
console.log('Mutation finished');
},
});
// Trigger mutation
mutation.mutate({ title: 'New Todo' });
// With async/await
mutation.mutateAsync({ title: 'New Todo' })
.then(data => console.log(data))
.catch(error => console.error(error));const mutation = useMutation({
mutationFn: (newTodo) => {
return fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
});
},
onSuccess: (data, variables, context) => {
console.log('成功!', data);
},
onError: (error, variables, context) => {
console.error('错误:', error);
},
onSettled: (data, error, variables, context) => {
console.log('变更操作完成');
},
});
// 触发变更
mutation.mutate({ title: 'New Todo' });
// 使用async/await
mutation.mutateAsync({ title: 'New Todo' })
.then(data => console.log(data))
.catch(error => console.error(error));import { useSuspenseQuery } from '@tanstack/react-query';
function TodoList() {
// This will suspend the component until data is ready
const { data } = useSuspenseQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
});
// No need for loading states - handled by Suspense boundary
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
// In parent component
function App() {
return (
<Suspense fallback={<div>Loading todos...</div>}>
<TodoList />
</Suspense>
);
}import { useSuspenseQuery } from '@tanstack/react-query';
function TodoList() {
// 组件会挂起直到数据准备完成
const { data } = useSuspenseQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
});
// 无需处理加载状态 - 由Suspense边界处理
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
// 在父组件中
function App() {
return (
<Suspense fallback={<div>加载待办事项...</div>}>
<TodoList />
</Suspense>
);
}references/infinite-queries.mduseInfiniteQuerygetNextPageParamgetPreviousPageParamreferences/infinite-queries.mduseInfiniteQuerygetNextPageParamgetPreviousPageParamreferences/optimistic-updates.mdreferences/optimistic-updates.mdreferences/typescript.mdreferences/typescript.mdreferences/query-invalidation.mdreferences/query-invalidation.mdreferences/performance.mdreferences/performance.mdnpm install @tanstack/react-query-devtoolsimport { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}npm install @tanstack/react-query-devtoolsimport { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}// First query
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
// Second query depends on first
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: () => fetchProjects(user.id),
enabled: !!user?.id, // Only run when user.id is available
});// 第一个查询
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
// 第二个查询依赖第一个查询的结果
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: () => fetchProjects(user.id),
enabled: !!user?.id, // 仅当user.id可用时执行
});function Dashboard() {
const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
const stats = useQuery({ queryKey: ['stats'], queryFn: fetchStats });
if (users.isLoading || posts.isLoading || stats.isLoading) {
return <div>Loading...</div>;
}
// All queries succeeded
return <DashboardView users={users.data} posts={posts.data} stats={stats.data} />;
}function Dashboard() {
const users = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
const posts = useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
const stats = useQuery({ queryKey: ['stats'], queryFn: fetchStats });
if (users.isLoading || posts.isLoading || stats.isLoading) {
return <div>加载中...</div>;
}
// 所有查询成功
return <DashboardView users={users.data} posts={posts.data} stats={stats.data} />;
}useQueriesimport { useQueries } from '@tanstack/react-query';
function TodoLists({ listIds }) {
const results = useQueries({
queries: listIds.map((id) => ({
queryKey: ['list', id],
queryFn: () => fetchList(id),
})),
});
const isLoading = results.some(result => result.isLoading);
const data = results.map(result => result.data);
return <Lists data={data} />;
}useQueriesimport { useQueries } from '@tanstack/react-query';
function TodoLists({ listIds }) {
const results = useQueries({
queries: listIds.map((id) => ({
queryKey: ['list', id],
queryFn: () => fetchList(id),
})),
});
const isLoading = results.some(result => result.isLoading);
const data = results.map(result => result.data);
return <Lists data={data} />;
}const queryClient = useQueryClient();
// Prefetch on hover
function TodoListLink({ id }) {
const prefetch = () => {
queryClient.prefetchQuery({
queryKey: ['todo', id],
queryFn: () => fetchTodo(id),
staleTime: 1000 * 60 * 5, // Cache for 5 minutes
});
};
return (
<Link to={`/todo/${id}`} onMouseEnter={prefetch}>
View Todo
</Link>
);
}const queryClient = useQueryClient();
// 鼠标悬停时预获取
function TodoListLink({ id }) {
const prefetch = () => {
queryClient.prefetchQuery({
queryKey: ['todo', id],
queryFn: () => fetchTodo(id),
staleTime: 1000 * 60 * 5, // 缓存5分钟
});
};
return (
<Link to={`/todo/${id}`} onMouseEnter={prefetch}>
查看待办事项
</Link>
);
}function TodoDetail({ todoId, initialTodo }) {
const { data } = useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetchTodo(todoId),
initialData: initialTodo, // Use this data immediately
staleTime: 1000 * 60, // Consider fresh for 1 minute
});
return <div>{data.title}</div>;
}function TodoDetail({ todoId, initialTodo }) {
const { data } = useQuery({
queryKey: ['todo', todoId],
queryFn: () => fetchTodo(todoId),
initialData: initialTodo, // 立即使用该数据
staleTime: 1000 * 60, // 1分钟内视为新鲜数据
});
return <div>{data.title}</div>;
}const { data, isPlaceholderData } = useQuery({
queryKey: ['todos', page],
queryFn: () => fetchTodos(page),
placeholderData: (previousData) => previousData, // Keep previous data while loading
});
// Or use static placeholder
const { data } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
placeholderData: { items: [], total: 0 },
});const { data, isPlaceholderData } = useQuery({
queryKey: ['todos', page],
queryFn: () => fetchTodos(page),
placeholderData: (previousData) => previousData, // 加载时保留之前的数据
});
// 或使用静态占位数据
const { data } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
placeholderData: { items: [], total: 0 },
});const { error, isError } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
if (isError) {
return <div>Error: {error.message}</div>;
}const { error, isError } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});
if (isError) {
return <div>错误:{error.message}</div>;
}const queryClient = new QueryClient({
defaultOptions: {
queries: {
onError: (error) => {
console.error('Query error:', error);
// Show toast notification, etc.
},
},
mutations: {
onError: (error) => {
console.error('Mutation error:', error);
},
},
},
});const queryClient = new QueryClient({
defaultOptions: {
queries: {
onError: (error) => {
console.error('查询错误:', error);
// 显示提示通知等
},
},
mutations: {
onError: (error) => {
console.error('变更错误:', error);
},
},
},
});import { useQuery } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';
function TodoList() {
const { data } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
throwOnError: true, // Throw errors to error boundary
});
return <div>{/* render data */}</div>;
}
function App() {
return (
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<TodoList />
</ErrorBoundary>
);
}import { useQuery } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';
function TodoList() {
const { data } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
throwOnError: true, // 将错误抛给错误边界
});
return <div>{/* 渲染数据 */}</div>;
}
function App() {
return (
<ErrorBoundary fallback={<div>出现错误</div>}>
<TodoList />
</ErrorBoundary>
);
}['todos', 'list', { filters }]staleTime: InfinitystaleTime: 0staleTime: 1000 * 60 * 5isLoadingerrorrefetchOnWindowFocus: falsestaleTimerefetchInterval['todos', 'list', { filters }]staleTime: InfinitystaleTime: 0staleTime: 1000 * 60 * 5isLoadingerrorrefetchOnWindowFocus: falsestaleTimerefetchIntervalcacheTimegcTimeuseInfiniteQueryuseSuspenseQuerycacheTimegcTimeuseInfiniteQueryuseSuspenseQuery