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

Redux嵌套对象更新更优语法:简化reducer操作的方案咨询

简化深层嵌套Redux Reducer的更新逻辑

问题场景

我当前有一个处理深层嵌套状态的reducer函数,写法冗长且可读性极差:

_({ 
  expandAbility: (state, a: { which: string }) => ({ 
    ...state, 
    report: state.report && { 
      waitingForIt: false, 
      content: state.report.content && { 
        ...state.report.content, 
        interaction: { 
          expandAbilities: !state.report.content.interaction.expandAbilities.contains(a.which) 
            ? state.report.content.interaction.expandAbilities.add(a.which) 
            : state.report.content.interaction.expandAbilities.remove(a.which) 
        } 
      } 
    } 
  }), 
}) 

对应的State类型参考:

const initialState = { 
  report: undefined as | { 
    waitingForIt?: boolean 
    content?: { 
      supportedFightIds: number[] 
      deathCut: number 
      reportInfo: any 
      deathsFull: any 
      deathsByPlayer: any 
      deathsByAbility: any 
      interaction: { expandAbilities: Set<string> } 
    } 
  } | undefined, 
  error: undefined as Error | undefined 
} 

除了创建变量引用深层路径之外,有没有什么技巧或流行库能让这类更新操作的写法更简洁、易读?


回答

嘿,这种嵌套地狱式的reducer写法确实让人头疼!除了用变量拆解路径,有不少实用的方案能帮你简化代码,我来分享几个当前主流的选项:

1. Immer + Redux Toolkit(最推荐)

Immer是现在处理不可变状态的行业标准,而Redux Toolkit(RTK)已经默认集成了它,完全不用额外配置。它允许你用"直接修改"的语法写逻辑,背后会自动帮你生成不可变的新状态,彻底告别层层展开的痛苦。

用RTK改写你的expandAbility reducer会简洁太多:

import { createSlice } from '@reduxjs/toolkit'

const reportSlice = createSlice({
  name: 'report',
  initialState,
  reducers: {
    expandAbility: (state, action: { payload: { which: string } }) => {
      // 先做空值判断,避免报错
      if (!state.report || !state.report.content) return;
      
      // 直接修改状态,Immer会处理不可变性
      state.report.waitingForIt = false;
      const { which } = action.payload;
      const abilitiesSet = state.report.content.interaction.expandAbilities;
      
      if (abilitiesSet.has(which)) {
        abilitiesSet.delete(which);
      } else {
        abilitiesSet.add(which);
      }
    }
  }
})

// 导出action和reducer
export const { expandAbility } = reportSlice.actions;
export default reportSlice.reducer;

如果单独用Immer(不使用RTK),写法也类似:

import { produce } from 'immer'

// 在原reducer中使用produce
expandAbility: produce((state, a: { which: string }) => {
  if (!state.report || !state.report.content) return;
  state.report.waitingForIt = false;
  const abilities = state.report.content.interaction.expandAbilities;
  abilities.has(a.which) ? abilities.delete(a.which) : abilities.add(a.which);
})

2. Ramda的透镜(Lenses)

如果你偏好函数式编程风格,Ramda的透镜工具可以让你精准聚焦到深层嵌套的状态路径,然后进行更新操作,避免手动处理层层展开。

示例代码:

import { lensPath, over, set } from 'ramda';

// 定义指向expandAbilities的透镜
const expandAbilitiesLens = lensPath(['report', 'content', 'interaction', 'expandAbilities']);
// 定义指向report.waitingForIt的透镜
const reportWaitingLens = lensPath(['report', 'waitingForIt']);

// 编写切换能力的纯函数
const toggleAbility = (which: string) => (abilities: Set<string>) => {
  const newSet = new Set(abilities);
  abilities.has(which) ? newSet.delete(which) : newSet.add(which);
  return newSet;
};

// 在reducer中组合使用
expandAbility: (state, a: { which: string }) => {
  // 先处理waitingForIt的更新
  const withWaitingUpdated = state.report ? set(reportWaitingLens, false, state) : state;
  // 再处理能力集合的切换
  return over(expandAbilitiesLens, toggleAbility(a.which), withWaitingUpdated);
}

3. 纯JS/TS解构优化(无额外依赖)

如果不想引入任何新库,通过解构和局部变量的方式,也能大幅提升原代码的可读性,比直接嵌套展开清爽很多:

expandAbility: (state, a: { which: string }) => {
  // 空值判断,直接返回原状态
  if (!state.report || !state.report.content) return state;
  
  // 解构出各层局部变量,避免重复写长路径
  const { report } = state;
  const { content } = report;
  const { interaction } = content;
  const { expandAbilities: oldAbilities } = interaction;
  
  // 创建新的Set,保证不可变性
  const newAbilities = new Set(oldAbilities);
  if (newAbilities.has(a.which)) {
    newAbilities.delete(a.which);
  } else {
    newAbilities.add(a.which);
  }
  
  // 逐层展开更新
  return {
    ...state,
    report: {
      ...report,
      waitingForIt: false,
      content: {
        ...content,
        interaction: {
          ...interaction,
          expandAbilities: newAbilities
        }
      }
    }
  };
}

总结

如果是基于Redux开发,Redux Toolkit + Immer绝对是当前的最佳实践——它不仅简化了代码,还内置了很多Redux的最佳实践,社区支持也最完善。Ramda适合喜欢函数式编程的场景,而纯解构优化则适合不想增加依赖的小型项目。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:09:38