React之react-query-devtools

半兽人 发表于: 2025-07-25   最后更新时间: 2025-08-05 10:30:37  
{{totalSubscript}} 订阅, 111 游览

一、@tanstack/react-query-devtools 是干什么的?

这是 React Query 的调试工具面板,可以:

  • 查看当前缓存中有哪些 query
  • 查看每个 query 的状态(loading、error、stale 等)
  • 手动触发刷新、失效
  • 查看 queryKey、数据内容、错误信息等

1. 安装

npm install @tanstack/react-query-devtools

2. 使用方法(和 Provider 一起加)

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} /> {/* 开关面板 */}
    </QueryClientProvider>
  )
}

添加后,开发环境中右下角会出现一个 React Query 图标,点开即可查看所有 query 状态。

二、分页加载 vs 无限滚动

我们这里演示一个 分页加载(点击按钮翻页)无限滚动(滚动到底加载更多) 的示例。

示例场景:

假设有一个 API 支持分页:

GET /api/posts?page=1
返回:
{
  data: [{ id, title }...],
  nextPage: 2,
  hasNextPage: true
}

三、分页加载示例(点击“加载更多”)

import { useInfiniteQuery } from '@tanstack/react-query'

const fetchPosts = async ({ pageParam = 1 }) => {
  const res = await fetch(`/api/posts?page=${pageParam}`);
  if (!res.ok) throw new Error('获取失败');
  return res.json();  // 返回 { data, nextPage, hasNextPage }
};

function PostList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isLoading,
    isError,
    error
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage, allPages) => {
      return lastPage.hasNextPage ? lastPage.nextPage : undefined;
    }
  });

  if (isLoading) return <p>加载中...</p>;
  if (isError) return <p>错误: {error.message}</p>;

  return (
    <div>
      {data.pages.map((page, i) => (
        <React.Fragment key={i}>
          {page.data.map(post => (
            <div key={post.id}>{post.title}</div>
          ))}
        </React.Fragment>
      ))}

      <button
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage || isFetchingNextPage}
      >
        {isFetchingNextPage ? '加载中...' : hasNextPage ? '加载更多' : '没有更多了'}
      </button>
    </div>
  );
}

四、无限滚动(IntersectionObserver 方式)

除了按钮点击,我们也可以让它“滑到底部自动加载下一页”:

import { useRef, useEffect } from 'react';

function PostList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    isLoading,
    isError,
    error
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage) =>
      lastPage.hasNextPage ? lastPage.nextPage : undefined,
  });

  const loader = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      if (entries[0].isIntersecting && hasNextPage) {
        fetchNextPage();
      }
    });

    if (loader.current) {
      observer.observe(loader.current);
    }

    return () => {
      if (loader.current) observer.unobserve(loader.current);
    };
  }, [fetchNextPage, hasNextPage]);

  if (isLoading) return <p>加载中...</p>;
  if (isError) return <p>错误: {error.message}</p>;

  return (
    <div>
      {data.pages.map((page, i) => (
        <React.Fragment key={i}>
          {page.data.map(post => (
            <div key={post.id}>{post.title}</div>
          ))}
        </React.Fragment>
      ))}

      <div ref={loader}>
        {isFetchingNextPage ? '加载中...' : hasNextPage ? '继续滑动加载更多' : '到底了'}
      </div>
    </div>
  );
}

五、注意事项

  • 所有分页逻辑靠 getNextPageParam 决定是否加载下一页
  • pages 是二维数组:data.pages[n].data[]
  • 服务端必须支持分页传参与返回
  • 如果用的是 Next.js,API 路由 /api/posts?page=x 可以自己 mock 一下数据
更新于 2025-08-05

查看React更多相关的文章或提一个关于React的问题,也可以与我们一起分享文章