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

React中如何用reducer更新嵌套对象值?含Immer方案及问题排查

解决嵌套表单字段更新问题及Immer使用指南

先修复当前的bug

1. 拼写错误导致undefined

你的update-contact reducer分支里,把action.payload拼成了action.paylod(少了字母a),这直接导致赋值的是undefined。修正后:

case 'update-contact':
  return { ...prevState, contact: { ...prevState.contact, [action.field]: action.payload}}

2. 字段归属错误

你例子里更新street1时用了update-contact类型,但street1属于address对象,应该用update-address类型:

onChange={(e) => { dispatch({ type: 'update-address', field: 'street1', payload: e.target.value })}}

简化嵌套字段更新的通用方案

如果不想写大量重复的case,可以改成一个通用的更新action,通过指定字段路径(比如address.street1、contact.firstName)来更新:

const formReducer = (prevState, action) => {
  if (action.type === 'update-field') {
    const path = action.field.split('.');
    const newState = { ...prevState };
    let current = newState;
    // 遍历路径到倒数第二层,逐层解构复制
    for (let i = 0; i < path.length - 1; i++) {
      current[path[i]] = { ...current[path[i]] };
      current = current[path[i]];
    }
    // 给最后一层字段赋值
    current[path[path.length - 1]] = action.payload;
    return newState;
  }
  return prevState;
}

使用时直接传对应路径:

// 更新address.street1
onChange={(e) => dispatch({ type: 'update-field', field: 'address.street1', payload: e.target.value })}
// 更新contact.firstName
onChange={(e) => dispatch({ type: 'update-field', field: 'contact.firstName', payload: e.target.value })}
// 更新details.companyManager.email
onChange={(e) => dispatch({ type: 'update-field', field: 'details.companyManager.email', payload: e.target.value })}

Immer库的使用方法

Immer的核心是produce函数,允许你用"可变"的写法修改状态,它会自动帮你生成不可变的新状态,彻底省去手动解构嵌套对象的麻烦。

1. 安装Immer

npm install immer
# 或
yarn add immer

2. 改造reducer

用Immer的produce包裹reducer逻辑,直接修改draft对象即可,无需手动复制解构:

import { produce } from 'immer';

const formReducer = produce((draft, action) => {
  switch (action.type) {
    case 'update-name':
      draft.name = action.payload;
      break;
    case 'update-address':
      draft.address[action.field] = action.payload;
      break;
    case 'update-contact':
      draft.contact[action.field] = action.payload;
      break;
    case 'update-client-id':
      draft.clientId = action.payload;
      break;
    case 'update-isActive':
      draft.isActive = action.payload;
      break;
    case 'update-requiresAction':
      draft.requiresAction = action.payload;
      break;
    case 'update-details':
      draft.details[action.field] = action.payload;
      break;
    // 加入通用更新逻辑
    case 'update-field':
      const path = action.field.split('.');
      let current = draft;
      for (let i = 0; i < path.length - 1; i++) {
        current = current[path[i]];
      }
      current[path[path.length - 1]] = action.payload;
      break;
  }
});

3. 极致简洁写法

甚至可以完全省去switch,只保留通用更新逻辑:

const formReducer = produce((draft, action) => {
  if (action.type === 'update-field') {
    const path = action.field.split('.');
    let current = draft;
    for (let i = 0; i < path.length - 1; i++) {
      current = current[path[i]];
    }
    current[path[path.length - 1]] = action.payload;
  }
});

使用方式和通用方案完全一致,代码量大幅减少,再也不用担心嵌套更新时的解构遗漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 09:03:19