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

如何用React Query v4替代Context并解决TS类型问题

使用React Query v4替代Context管理用户状态:解决userObject可能为undefined的类型问题

核心问题分析

你的useUser Hook查询逻辑中始终会返回User实例,但React Query的UseQueryResult类型中data字段默认是可选的(加载阶段或查询失败时为undefined),这导致TypeScript报错。你希望让Hook能像useContext一样直接返回确定的User对象,同时保留React Query的缓存优势。


解决方案一:修改useUser Hook,封装状态与确定的User对象

直接在useUser中处理加载、错误状态,返回包含确定User的结构化结果,既满足类型安全,又让组件能优雅处理状态:

function useUser() {
  const query = getInitialUserUrl;
  const platform = usePlatformContext();
  const { data, isLoading, isError, error } = useQuery<User, Error>(
    queryKeyUseUser,
    async () => {
      const data = await fetchAuth(query);
      return didQueryReturnData(data) 
        ? new User(platform, data[0]) 
        : new User(platform);
    },
    {
      refetchOnReconnect: 'always',
      refetchInterval: false,
    },
  );

  // 兜底生成空User,确保user永远不为undefined
  const user = data ?? new User(platform);

  return { user, isLoading, isError, error };
}

export default useUser;

使用时,组件可以直接获取user,同时根据isLoading/isError显示对应UI:

const useMutationVerifyEmail = () => {
  const { user } = useUser();

  return useMutation(
    (data: FormEmailVerifyInput) => verifyEmail(user.id, data.validation_code)
  );
};

解决方案二:基于Suspense的包装Hook(适合已启用Suspense的项目)

如果你的项目使用React Suspense处理加载状态,可以开启React Query的suspense选项,让包装Hook直接返回确定的User:

首先修改useUser支持Suspense:

function useUser() {
  const query = getInitialUserUrl;
  const platform = usePlatformContext();
  return useQuery<User, Error>(
    queryKeyUseUser,
    async () => {
      const data = await fetchAuth(query);
      return didQueryReturnData(data) 
        ? new User(platform, data[0]) 
        : new User(platform);
    },
    {
      refetchOnReconnect: 'always',
      refetchInterval: false,
      suspense: true, // 开启Suspense
    },
  );
}

然后编写包装Hook:

function useUserObject() {
  const { data: userObject, error } = useUser();

  // 查询失败时抛出错误,由ErrorBoundary捕获
  if (error || !userObject) {
    throw error ?? new Error('获取账户信息失败!');
  }

  return userObject;
}

使用时,组件需要被Suspense和ErrorBoundary包裹,加载时显示fallback,错误时显示错误提示。


关于你提出的包装Hook方案的优化建议

你当前的包装Hook直接抛出错误会导致组件崩溃,这不是最优解——更好的做法是在组件层面处理加载和错误状态,而不是让Hook直接抛出错误。如果一定要用包装Hook,建议加入状态判断,避免在加载阶段抛出错误,或者结合Suspense/ErrorBoundary实现优雅降级。

至于是否需要包装一层:这种封装是合理的,它能把React Query的状态逻辑隐藏起来,让组件使用体验更接近useContext,同时保留React Query的缓存、自动重取等核心能力。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:15:34