React Query自定义hook无法在Redux Saga中调用,求解决办法
我正将部分无需保留在Redux中的数据从Redux Store迁移至React Query,已将国家数组从Store移至一个通过React Query调用API的自定义hook中。
现在另一个Redux Saga需要使用该国家数组,但无法在Saga中调用hook,触发如下报错:
Error: React Hook "useCountries" is called in function "onSearchInit" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use". react-hooks/rules-of-hooks
我的hook导入方式如下:
import { useCountries } from "@mysite/hooks/useCountries";
使用方式如下:
const { countries } = useCountries();
我对Redux并不精通,请问是否有其他方法可将hook中的数据传入Saga?
方法1:抽离独立的请求函数复用逻辑
React Query的hook只是封装了请求+缓存的逻辑,你可以把核心的API请求代码单独抽成独立函数,让Saga直接调用:
- 重构
useCountrieshook,拆分请求逻辑:
// @mysite/hooks/useCountries.js export const fetchCountries = async () => { const res = await fetch('/api/countries'); // 替换为你的实际API地址 if (!res.ok) throw new Error('获取国家数据失败'); return res.json(); }; export const useCountries = () => { return useQuery({ queryKey: ['countries'], queryFn: fetchCountries, }); };
- 在Saga中直接调用这个函数:
import { fetchCountries } from "@mysite/hooks/useCountries"; import { call, put } from 'redux-saga/effects'; function* onSearchInit() { try { const countries = yield call(fetchCountries); // 拿到数据后执行后续逻辑,比如存入Store或直接使用 yield put({ type: 'SEARCH_INIT_SUCCESS', payload: { countries } }); } catch (err) { yield put({ type: 'SEARCH_INIT_FAILED', payload: err.message }); } }
⚠️ 注意:这种方式Saga会单独发起请求,不会复用React Query的缓存。如果需要复用缓存,参考其他方法。
方法2:通过React组件同步数据到Redux Store
在已挂载的React组件中(比如根组件或页面组件),用useCountries获取数据后dispatch到Redux Store,Saga再从Store中读取:
- 组件中同步数据:
import { useCountries } from "@mysite/hooks/useCountries"; import { useDispatch } from 'react-redux'; import { useEffect } from 'react'; function App() { const dispatch = useDispatch(); const { data: countries } = useCountries(); useEffect(() => { if (countries) { dispatch({ type: 'SET_COUNTRIES', payload: countries }); } }, [countries, dispatch]); // 组件其他逻辑... }
- Saga中通过
select获取Store中的数据:
import { select, put } from 'redux-saga/effects'; function* onSearchInit() { const countries = yield select(state => state.global.countries); // 替换为你的实际Store路径 if (countries) { // 执行搜索初始化逻辑 yield put({ type: 'DO_SEARCH', payload: { countries } }); } }
这种方式能复用React Query的缓存,适合不需要实时发起请求的场景。
方法3:直接访问React Query的缓存实例
如果项目中已经全局配置了React Query Client,可以直接在Saga中读取缓存数据:
- 确保Query Client全局可访问:
// src/utils/reactQueryClient.js import { QueryClient } from '@tanstack/react-query'; export const queryClient = new QueryClient();
- 在Saga中读取缓存:
import { queryClient } from '../utils/reactQueryClient'; import { call, put } from 'redux-saga/effects'; import { fetchCountries } from "@mysite/hooks/useCountries"; function* onSearchInit() { // 先尝试从缓存获取 let countries = queryClient.getQueryData(['countries']); if (!countries) { // 缓存没有则发起请求,并存入缓存 countries = yield call(fetchCountries); queryClient.setQueryData(['countries'], countries); } // 使用countries执行后续逻辑 yield put({ type: 'SEARCH_INIT', payload: { countries } }); }
这种方式兼顾了缓存复用和Saga直接获取数据的需求,适合需要灵活操作缓存的场景。
内容的提问来源于stack exchange,提问作者ComCool

