Redux中仅修改games数组却导致InitialGames数组被意外修改
问题原因及修复方案
核心问题:引用类型特性与Redux纯函数原则违反
你遇到的问题本质是JavaScript引用类型的特性,加上违反了Redux reducer的纯函数要求:
ADD_GAME中,games和InitialGames存入的是同一个action.payload.game对象的引用,并非独立副本,修改其中一个数组内的对象属性时,另一个数组里的对应对象会同步变化。PLAYER_DEAD里let newGames = state.games只是赋值原数组的引用,没有创建新数组,后续splice操作直接修改了原数组内容,同时因为InitialGames里的游戏对象与games里的是同一引用,导致InitialGames也被改动。RESET_GAME直接修改原state.games,违反了Redux reducer不能直接修改原状态的规则。PUT_INFOS返回状态时未保留原有games和InitialGames,会导致状态丢失。
修复步骤
- 深拷贝游戏对象:让
games和InitialGames存储完全独立的对象副本,切断引用关联。 - 操作前创建新数组/对象:所有状态修改都基于拷贝后的新对象进行,避免直接修改原状态。
- 保留原有状态属性:返回新状态时,通过扩展运算符保留未修改的状态字段。
修复后的Reducer代码
import { ADD_GAME, PLAYER_DEAD, PUT_INFOS, RESET_GAME } from "./actions"; const initialState = { games: [], InitialGames: [], }; // 深拷贝游戏对象(若对象含JSON无法序列化的内容,可改用lodash.cloneDeep) const deepCloneGame = (game) => JSON.parse(JSON.stringify(game)); export default (state = initialState, action) => { switch (action.type) { case RESET_GAME: const clonedInitialGame = deepCloneGame(state.InitialGames[action.payload.gameIndex]); return { ...state, games: state.games.map((game, index) => index === action.payload.gameIndex ? clonedInitialGame : game ) }; case ADD_GAME: const clonedGame = deepCloneGame(action.payload.game); return { ...state, games: [...state.games, clonedGame], InitialGames: [...state.InitialGames, clonedGame], }; case PUT_INFOS: return { ...state, gameInfos: action.gameInfos, }; case PLAYER_DEAD: // 深拷贝整个games数组,确保不影响原状态和InitialGames const newGames = state.games.map(game => deepCloneGame(game)); newGames[action.payload.indexGame].teams[action.payload.index].players.splice(0, 1); return { ...state, games: newGames }; default: return state; } };
补充说明
如果游戏对象层级复杂(包含函数、Symbol等),可以使用lodash.cloneDeep实现深拷贝,需先安装lodash依赖。始终遵守Redux核心规则:reducer必须是纯函数,禁止直接修改原状态,必须返回新状态对象。
内容的提问来源于stack exchange,提问作者Zaki Kendil
相关产品推荐
相关产品推荐

