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

如何在React函数组件中用useReducer更新嵌套对象状态且不丢失值?

问题分析与修复方案

核心问题

  1. reducer参数错误:原reducer仅接收单个参数,正确的useReducer reducer签名应为(state, action),第一个参数是当前状态,第二个是dispatch传递的动作标识。
  2. 状态结构被破坏:
    • Strength_Inc分支中,使用Object.keys(state['SPECIAL']).map(...)返回数组,完全改变了state的原始结构(原state是包含SPECIAL、Skills、Stats的对象),导致后续渲染时state.SPECIAL不存在,所有内容丢失。
    • 其他Inc分支仅返回SPECIAL对象的副本,丢失了Skills和Stats属性,点击后state不再包含这两个字段,对应渲染内容消失。
  3. 缺少减号动作处理:页面中的减号按钮会dispatchkey + '_Dec'动作,但reducer中未实现对应逻辑。

修复后的完整代码

import { useReducer } from 'react';

function App(){
  const [state, dispatch] = useReducer(reducer, 
  {
    SPECIAL: {
      Strength: 5,
      Perception: 5,
      Endurance: 5,
      Charisma: 5,
      Intelligence: 5,
      Agility: 5,
      Luck: 5},
    Skills: {
      Barter: 3,
      Energy_Weapons: 3,
      Explosives: 3,
      Guns: 3,
      Lockpick: 3,
      Medicine: 3,
      Melee_Weapons: 3,
      Repair: 3,
      Science: 3,
      Sneak: 3,
      Speech: 3,
      Survival: 3,
      Unarmed: 3},
    Stats: {
      Action_Points: 65,
      Carry_Weight: 150,
      Critical_Chance: 0,
      Damage_Resistance: 0,
      Damage_Threshold: 0,
      Hit_Points: 100,
      Melee_Damage: 0,
      Rad_Resistance: 2,
      Skill_Rate: 0,
      Unarmed_Damage: 0}
  })

  function reducer(state, action){
    const [statName, operation] = action.split('_');
    
    switch (action){
      // 处理所有加值动作
      case 'Strength_Inc':
      case 'Perception_Inc':
      case 'Endurance_Inc':
      case 'Charisma_Inc':
      case 'Intelligence_Inc':
      case 'Agility_Inc':
      case 'Luck_Inc':
        return {
          ...state, // 保留原状态的Skills和Stats
          SPECIAL: {
            ...state.SPECIAL, // 复制原SPECIAL对象
            [statName]: state.SPECIAL[statName] + 1 // 修改目标属性值
          }
        }
      // 处理所有减值动作,增加下限判断
      case 'Strength_Dec':
      case 'Perception_Dec':
      case 'Endurance_Dec':
      case 'Charisma_Dec':
      case 'Intelligence_Dec':
      case 'Agility_Dec':
      case 'Luck_Dec':
        if (state.SPECIAL[statName] <= 0) return state;
        return {
          ...state,
          SPECIAL: {
            ...state.SPECIAL,
            [statName]: state.SPECIAL[statName] - 1
          }
        }
      default:
        return state
    }
  }

  return (
    <div style={{
      color: "green",
      display: "block"
    }}>
      {Object.entries(state.SPECIAL).map(([key, value]) => (
        <p className='SPECIAL'> 
          <button onClick={() => dispatch(key + '_Inc')}>+</button> 
          {value} 
          <button onClick={() => dispatch(key + '_Dec')}>-</button> 
          {key} 
        </p>
      ))}
      {Object.entries(state.Skills).map(([key, value]) => (
        <p className='Skills'> {value} {key} </p>
      ))}
      {Object.entries(state.Stats).map(([key, value]) => (
        <p className='Stats'> {value} {key} </p>
      ))}
    </div>
  )
};

export default App;

修复要点

  • 每次返回完整的state对象,通过扩展运算符...state保留Skills和Stats,再重新生成SPECIAL对象修改目标属性,确保状态结构一致。
  • 拆分action字符串统一处理Inc/Dec动作,减少重复代码。
  • 增加数值下限判断,避免属性值变为负数(可根据需求调整)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:35:16