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

React/Redux/Saga 分类接口无限请求循环问题求助

解决Redux Saga分类接口无限循环调用问题

问题背景

项目基于React/TypeScript、Redux和Saga开发,需获取分类列表渲染导航栏。通过useEffect分发Redux Action触发数据请求,但出现分类接口被调用数百次的无限循环问题,尝试Stack Overflow现有方案后仍未解决。

核心问题分析

无限循环的根源在于Action职责混淆+Saga监听逻辑错误:

  • 同一个GET_CATEGORIES Action既被用于触发Saga的API请求,又被用于将请求结果存入Reducer
  • Saga中使用takeEvery(GET_CATEGORIES, getCategoriesTree),意味着每次dispatch这个Action都会触发一次API请求
  • 当Saga请求完成后,会再次put(getCategories(categoryTreeDTO)),这又会触发Saga的监听逻辑,形成无限循环

此外,Reducer初始状态设为数组[],后续却更新为对象{...state, payload: action.payload},存在类型不一致问题,可能引发额外渲染。

修复步骤

1. 拆分Action类型与职责

新增专门用于触发请求的Action,将“触发请求”和“设置数据”的动作分离:

修改actionTypes.ts

export const FETCH_CATEGORIES = 'FETCH_CATEGORIES'; // 触发请求
export const SET_CATEGORIES = 'SET_CATEGORIES'; // 设置数据

修改Actions文件

import { FETCH_CATEGORIES, SET_CATEGORIES } from './actionTypes';

// 触发分类数据请求
export const fetchCategories = () => {
    return {
        type: FETCH_CATEGORIES,
    };
};

// 将请求到的分类数据存入Redux
export const setCategories = (categories: any) => {
    return {
        type: SET_CATEGORIES,
        payload: categories,
    };
};

2. 调整Saga监听与处理逻辑

让Saga监听新增的FETCH_CATEGORIES Action,请求完成后分发SET_CATEGORIES而非原Action:

修改Saga文件

import { FETCH_CATEGORIES } from './actionTypes';
import { setCategories } from './actions';

let categoriesApiService = container.resolve(CategoriesApiService);

const categoryApi = async () => {
    return firstValueFrom(
        categoriesApiService.GetCategoryTree({
            path: { version: '1' },
            query: {},
        })
    );
};

function* getCategoriesTree() {
    try {
        let categoryTreeDTO: CategoryTreeDTO = yield call(categoryApi);
        yield put(setCategories(categoryTreeDTO));
    } catch (error: any) {
        yield put(apiError(error?.response?.data?.message));
    }
}

export function* watchGetCategories() {
    yield takeEvery(FETCH_CATEGORIES, getCategoriesTree);
}

function* categorySaga() {
    yield all([fork(watchGetCategories)]);
}

export default categorySaga;

3. 修正Reducer初始状态与更新逻辑

将初始状态统一为对象类型,避免类型混乱:

修改Reducer文件

import { SET_CATEGORIES } from './actionTypes';

const initialState = {
    payload: [],
};

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case SET_CATEGORIES:
            return {
                ...state,
                payload: action.payload,
            };
        default:
            return state;
    }
};

export default reducer;

4. 调整组件中的dispatch逻辑

组件中分发fetchCategories触发请求,无需传递现有categories参数:

修改TSX组件代码

const dispatch = useDispatch();
const categories = useSelector((state) => state?.categories?.payload);

const loadCategories = () => {
    dispatch(fetchCategories());
};

useEffect(() => {
    loadCategories();
}, []);

{categories?.map((x, index) => (
    <Link key={index} href={`/store/${x.name}/s`}>
        <a
            type="button"
            id={x.name}
            title={x.name}
            className="xl:px-3 px-2 py-[1.15rem] font-normal"
        >
            {x.name}
        </a>
    </Link>
))}

额外优化点

  • 为map渲染的元素添加key属性,符合React渲染规范
  • Reducer默认分支无需拷贝原state,直接返回即可,减少不必要的状态更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:27:36