You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+TypeScript中基于React-Query的Query Provider开发需求

React+TypeScript 实现带状态渲染的Query Provider组件

1. 定义上下文与类型

先创建用于传递Query数据的上下文,同时提供类型保障:

import { ReactNode, createContext, useContext } from 'react';
import { UseQueryResult } from '@tanstack/react-query';

// 提取useQuery返回的核心属性作为上下文类型
type QueryContextValue<TData = unknown> = Pick<
  UseQueryResult<TData>,
  'data' | 'refetch' | 'isFetching'
>;

// 创建上下文,初始值设为null
const QueryContext = createContext<QueryContextValue | null>(null);

// 子组件用这个钩子获取上下文数据
export function useQueryData<TData = unknown>() {
  const context = useContext(QueryContext);
  if (!context) {
    throw new Error('useQueryData必须在Query组件内部使用');
  }
  return context as QueryContextValue<TData>;
}

2. 实现状态插槽组件

定义三个插槽组件,用于标记不同状态下要渲染的内容:

type QuerySlotProps = {
  children: ReactNode;
};

export function QuerySuccess({ children }: QuerySlotProps) {
  return <>{children}</>;
}

export function QueryError({ children }: QuerySlotProps) {
  return <>{children}</>;
}

export function QueryLoading({ children }: QuerySlotProps) {
  return <>{children}</>;
}

3. 核心Query Provider组件

接收useQuery返回的所有参数,自动根据状态渲染对应插槽,并通过上下文传递数据:

type QueryProps<TData = unknown> = UseQueryResult<TData> & {
  children: ReactNode;
};

export function Query<TData = unknown>({
  data,
  refetch,
  isFetching,
  isLoading,
  isError,
  error,
  children,
}: QueryProps<TData>) {
  // 从子元素中筛选对应状态的组件
  const childArray = React.Children.toArray(children);
  const successChild = childArray.find(child =>
    React.isValidElement(child) && child.type === QuerySuccess
  );
  const errorChild = childArray.find(child =>
    React.isValidElement(child) && child.type === QueryError
  );
  const loadingChild = childArray.find(child =>
    React.isValidElement(child) && child.type === QueryLoading
  );

  // 按状态分支渲染
  if (isLoading) {
    return loadingChild ?? <div>加载中...</div>;
  }

  if (isError) {
    return errorChild ?? <div>加载失败: {(error as Error).message}</div>;
  }

  // 成功状态下注入上下文,渲染成功组件
  return (
    <QueryContext.Provider value={{ data, refetch, isFetching }}>
      {successChild ?? null}
    </QueryContext.Provider>
  );
}

4. 使用示例

按照你给出的结构使用,子组件通过useQueryData获取数据:

// 假设这是你的自定义useQuery钩子
function useGetDashboardInfo() {
  return useQuery({
    queryKey: ['dashboardInfo'],
    queryFn: () => fetch('/api/dashboard').then(res => res.json()),
  });
}

export function DashboardView() {
  const queryData = useGetDashboardInfo();
  return (
    <Query {...queryData}>
      <QueryLoading>
        <div>仪表盘加载中...</div>
      </QueryLoading>
      <QuerySuccess>
        <DashboardComponent />
      </QuerySuccess>
      <QueryError>
        <div>仪表盘加载失败,请稍后重试</div>
      </QueryError>
    </Query>
  );
}

// 子组件中获取数据和刷新能力
function DashboardComponent() {
  const { data, refetch, isFetching } = useQueryData<{ stats: { todayViews: number; totalUsers: number } }>();

  return (
    <div>
      <h1>仪表盘</h1>
      {isFetching && <span>数据刷新中...</span>}
      <button onClick={() => refetch()}>刷新数据</button>
      {data && (
        <div>
          <p>今日访问量: {data.stats.todayViews}</p>
          <p>总用户数: {data.stats.totalUsers}</p>
        </div>
      )}
    </div>
  );
}

关键特性

  • 类型安全:通过泛型确保数据类型匹配,避免类型错误
  • 灵活定制:可自定义加载/错误/成功状态的展示内容,也可使用默认渲染
  • 无props drilling:子组件直接通过钩子获取数据和刷新方法,无需层层传递

内容的提问来源于stack exchange,提问作者szescian6

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:20:30