如何用React Query v4替代Context并解决TS类型问题
核心问题分析
你的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

