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

Redux及useReducer中拆分嵌套对象reducer逻辑的方法咨询

拆分嵌套状态的Reducer逻辑:Redux Toolkit与原生useReducer方案

一、Redux Toolkit 用Slice拆分逻辑

Redux Toolkit的createSlice完全可以拆分这种嵌套结构的逻辑,核心思路是把Card(Item)的顶层数组操作和Answer(subItem)的嵌套操作拆分到不同模块,让代码更模块化。

1. 拆分单独的Answer子Reducer

先写一个专门处理单个Card内部answers和targetAnswersId的子reducer,把所有嵌套相关的逻辑集中在这里:

// cardAnswerReducer.js
const cardAnswerReducer = (cardState, action) => {
  switch (action.type) {
    case 'cards/createAnswer': {
      const newAnswerId = Math.floor(Date.now()).toString();
      return {
        ...cardState,
        answers: [...cardState.answers, { id: newAnswerId, text: action.payload.text }]
      };
    }
    case 'cards/deleteAnswer': {
      return {
        ...cardState,
        answers: cardState.answers.filter(ans => ans.id !== action.payload.answerId)
      };
    }
    case 'cards/addTargetAnswerId': {
      return {
        ...cardState,
        targetAnswersId: [...cardState.targetAnswersId, action.payload.answerId]
      };
    }
    case 'cards/deleteTargetAnswerId': {
      return {
        ...cardState,
        targetAnswersId: cardState.targetAnswersId.filter(id => id !== action.payload.answerId)
      };
    }
    case 'cards/setQuestion': {
      return { ...cardState, question: action.payload.text };
    }
    case 'cards/setType': {
      return { ...cardState, type: action.payload.type };
    }
    default:
      return cardState;
  }
};

2. 用createSlice创建顶层Cards Slice

顶层slice只负责数组级别的增删操作,嵌套操作委托给子reducer处理:

// cardsSlice.js
import { createSlice } from '@reduxjs/toolkit';
import { cardAnswerReducer } from './cardAnswerReducer';

const initialState = [];

const cardsSlice = createSlice({
  name: 'cards',
  initialState,
  reducers: {
    addCard: (state) => {
      const newCardId = Math.floor(Date.now()).toString();
      state.push({
        id: newCardId,
        type: 'oneAnswer',
        question: '',
        answers: [],
        targetAnswersId: []
      });
    },
    deleteCard: (state, action) => {
      return state.filter(card => card.id !== action.payload);
    },
    // 统一处理单个Card的更新操作
    updateCard: (state, action) => {
      const targetCardIndex = state.findIndex(card => card.id === action.payload.id);
      if (targetCardIndex === -1) return state;
      state[targetCardIndex] = cardAnswerReducer(state[targetCardIndex], {
        type: `cards/${action.payload.actionType}`,
        payload: action.payload.data
      });
    }
  }
});

export const { addCard, deleteCard, updateCard } = cardsSlice.actions;
export default cardsSlice.reducer;

组件调用示例

在组件里通过updateCard统一触发单个Card的嵌套操作:

// 设置Card问题
dispatch(updateCard({
  id: cardId,
  actionType: 'setQuestion',
  data: { text: '新的问题内容' }
}));

// 创建新Answer
dispatch(updateCard({
  id: cardId,
  actionType: 'createAnswer',
  data: { text: '新选项内容' }
}));

二、原生React useReducer的拆分方案

原生useReducer同样可以实现逻辑拆分,核心是将数组级操作和单个Item的内部操作分离,用子reducer处理单个Item的状态更新。

1. 拆分子reducer处理单个Card

把单个Card的所有更新逻辑抽离到独立的reducer中:

// cardReducer.js
export const cardReducer = (card, action) => {
  switch (action.type) {
    case 'SET_CARD_QUESTION':
      return { ...card, question: action.payload.text };
    case 'SET_CARD_TYPE':
      return { ...card, type: action.payload.type };
    case 'CREATE_CARD_ANSWER': {
      const newAnswerId = Math.floor(Date.now()).toString();
      return {
        ...card,
        answers: [...card.answers, { id: newAnswerId, text: action.payload.text }]
      };
    }
    case 'DELETE_CARD_ANSWER':
      return {
        ...card,
        answers: card.answers.filter(ans => ans.id !== action.payload.answerId)
      };
    case 'ADD_TARGET_ANSWER_ID':
      return {
        ...card,
        targetAnswersId: [...card.targetAnswersId, action.payload.answerId]
      };
    case 'DELETE_TARGET_ANSWER_ID':
      return {
        ...card,
        targetAnswersId: card.targetAnswersId.filter(id => id !== action.payload.answerId)
      };
    default:
      return card;
  }
};

2. 顶层reducer调用子reducer

顶层reducer只负责数组的增删,所有单个Card的操作都委托给子reducer:

// rootReducer.js
import { cardReducer } from './cardReducer';

export const rootReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_CARD': {
      const newCardId = Math.floor(Date.now()).toString();
      return [...state, {
        id: newCardId,
        type: 'oneAnswer',
        question: '',
        answers: [],
        targetAnswersId: []
      }];
    }
    case 'DELETE_CARD': {
      if (!action.payload) throw new Error('DELETE_CARD需要传入payload');
      return state.filter(card => card.id !== action.payload);
    }
    // 所有单个Card的操作统一委托给子reducer
    case 'SET_CARD_QUESTION':
    case 'SET_CARD_TYPE':
    case 'CREATE_CARD_ANSWER':
    case 'DELETE_CARD_ANSWER':
    case 'ADD_TARGET_ANSWER_ID':
    case 'DELETE_TARGET_ANSWER_ID': {
      return state.map(card => {
        if (card.id === action.payload.id) {
          return cardReducer(card, action);
        }
        return card;
      });
    }
    default:
      throw new Error(`找不到对应的action类型: ${action.type}`);
  }
};

核心思路总结

不管是Redux Toolkit还是原生useReducer,拆分嵌套状态逻辑的关键都是:

  • 分离数组级别的Item增删操作和单个Item内部的subItem操作/字段更新
  • 用子reducer集中处理单个Item的状态变化,避免在顶层reducer中重复编写map/filter逻辑
  • Redux Toolkit的slice可以自动生成action类型和创建函数,进一步简化代码;原生useReducer则通过拆分reducer函数实现模块化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 02:27:03