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
相关产品推荐
相关产品推荐

