为什么我的 Redux Toolkit reducer 会改变状态?
Why is my Redux Toolkit reducer mutating state?
所以出于某种原因,下面的 equipItemById reducer 似乎是变异状态 - 尽管它基本上是 Redux Toolkit 示例中的逐字记录:
完整切片如下:
import { createSlice } from '@reduxjs/toolkit';
import Item from '../../../Entities/Item/Item';
const initialState = {
itemsById: [
new Item('weapon', 'oak_stave', 0),
new Item('chest', 'basic_robes', 0),
new Item('feet', 'basic_boots', 0),
new Item('head', 'basic_circlet', 0),
new Item('consumable', 'potion_of_healing', 0),
new Item('consumable', 'potion_of_healing', 1),
new Item('consumable', 'potion_of_healing', 1),
new Item('weapon', 'rusty_sword', 5),
new Item('weapon', 'iron_sword', 5),
new Item('weapon', 'steel_sword', 5),
new Item('weapon', 'enchanted_steel_sword', 5),
],
inTrade: false,
actorInTradeById: undefined,
itemsPlayerWantsToTradeById: [],
itemsOtherActorWantsToTrade: [],
itemsByLocationName: {
centralSquare: [new Item('weapon', 'enchanted_steel_sword')],
},
};
const itemSlice = createSlice({
name: 'items',
initialState: initialState,
reducers: {
addItemToActorFromLocationByIdAndName: (state, action) => {
const { actorId, item } = action.payload;
let itemCopy = item;
item.ownerId = actorId;
state.itemsById.push(itemCopy);
},
equipItemById: (state, action) => {
const item = state.itemsById.find((item) => item.id === action.payload);
item.equipped = true;
},
unequipItemById: (state, action) => {
const { itemId } = action.payload;
const item = state.itemsById.find((item) => item.id === itemId);
item.equipped = false;
},
dropItemFromInventory: (state, action) => {
const { itemId, locationName } = action.payload;
const item = state.itemsById.find(
(item) => item.id === itemId
);
item.ownerId = undefined;
item.equipped = false;
state.itemsById = state.itemsById.filter(item => item.id !== itemId);
state.itemsByLocationName[locationName].push(item);
},
removeItemFromLocation: (state, action) => {
const { itemId, locationName } = action.payload;
state.itemsByLocationName[locationName] = state.itemsByLocationName[
locationName
].filter((item) => item.id !== itemId);
},
},
});
export const {
addItemToActorFromLocationByIdAndName: addItemToActorById,
equipItemById,
unequipItemFromActorByIds,
inventorySetActiveItem,
equippedSetActiveItem,
dropItemFromInventory,
dropItemFromEquipped,
removeItemFromLocation,
} = itemSlice.actions;
export default itemSlice;
调度称为:
dispatch(
itemSlice.actions.equipItemById(props.item.id)
);
正如您在下面看到的那样 - 所讨论的操作在前后都具有相同的装备 属性 - 尽管之前肯定是错误的。
Item 对象看起来像这样(并且嵌套在 itemsById 数组中)
{
'0': {
type: 'weapon',
id: 0,
qty: 1,
equipped: true,
ownerId: 0,
name: 'Oak Branch',
icon: 'fa-staff',
rarity: 0,
descDetails: 'Deals an additional 1 damage per hit.',
desc: 'A simple weapon. Strong and sturdy. No man turns down a stout wooden branch, when the alternative is an empty hand to face the sharp steel of a bandit.',
stats: {},
value: 1,
slot: 'weapon_main',
addedDmgString: '1'
}
}
默认情况下,immer
不适用于 class 个实例。不过,您可以将它们标记为 immerable
。
但一般来说,您首先不应将 class 个实例放入您的 Redux 存储中,请参阅 the Redux Style Guide
所以出于某种原因,下面的 equipItemById reducer 似乎是变异状态 - 尽管它基本上是 Redux Toolkit 示例中的逐字记录:
完整切片如下:
import { createSlice } from '@reduxjs/toolkit';
import Item from '../../../Entities/Item/Item';
const initialState = {
itemsById: [
new Item('weapon', 'oak_stave', 0),
new Item('chest', 'basic_robes', 0),
new Item('feet', 'basic_boots', 0),
new Item('head', 'basic_circlet', 0),
new Item('consumable', 'potion_of_healing', 0),
new Item('consumable', 'potion_of_healing', 1),
new Item('consumable', 'potion_of_healing', 1),
new Item('weapon', 'rusty_sword', 5),
new Item('weapon', 'iron_sword', 5),
new Item('weapon', 'steel_sword', 5),
new Item('weapon', 'enchanted_steel_sword', 5),
],
inTrade: false,
actorInTradeById: undefined,
itemsPlayerWantsToTradeById: [],
itemsOtherActorWantsToTrade: [],
itemsByLocationName: {
centralSquare: [new Item('weapon', 'enchanted_steel_sword')],
},
};
const itemSlice = createSlice({
name: 'items',
initialState: initialState,
reducers: {
addItemToActorFromLocationByIdAndName: (state, action) => {
const { actorId, item } = action.payload;
let itemCopy = item;
item.ownerId = actorId;
state.itemsById.push(itemCopy);
},
equipItemById: (state, action) => {
const item = state.itemsById.find((item) => item.id === action.payload);
item.equipped = true;
},
unequipItemById: (state, action) => {
const { itemId } = action.payload;
const item = state.itemsById.find((item) => item.id === itemId);
item.equipped = false;
},
dropItemFromInventory: (state, action) => {
const { itemId, locationName } = action.payload;
const item = state.itemsById.find(
(item) => item.id === itemId
);
item.ownerId = undefined;
item.equipped = false;
state.itemsById = state.itemsById.filter(item => item.id !== itemId);
state.itemsByLocationName[locationName].push(item);
},
removeItemFromLocation: (state, action) => {
const { itemId, locationName } = action.payload;
state.itemsByLocationName[locationName] = state.itemsByLocationName[
locationName
].filter((item) => item.id !== itemId);
},
},
});
export const {
addItemToActorFromLocationByIdAndName: addItemToActorById,
equipItemById,
unequipItemFromActorByIds,
inventorySetActiveItem,
equippedSetActiveItem,
dropItemFromInventory,
dropItemFromEquipped,
removeItemFromLocation,
} = itemSlice.actions;
export default itemSlice;
调度称为:
dispatch(
itemSlice.actions.equipItemById(props.item.id)
);
正如您在下面看到的那样 - 所讨论的操作在前后都具有相同的装备 属性 - 尽管之前肯定是错误的。
Item 对象看起来像这样(并且嵌套在 itemsById 数组中)
{
'0': {
type: 'weapon',
id: 0,
qty: 1,
equipped: true,
ownerId: 0,
name: 'Oak Branch',
icon: 'fa-staff',
rarity: 0,
descDetails: 'Deals an additional 1 damage per hit.',
desc: 'A simple weapon. Strong and sturdy. No man turns down a stout wooden branch, when the alternative is an empty hand to face the sharp steel of a bandit.',
stats: {},
value: 1,
slot: 'weapon_main',
addedDmgString: '1'
}
}
immer
不适用于 class 个实例。不过,您可以将它们标记为 immerable
。
但一般来说,您首先不应将 class 个实例放入您的 Redux 存储中,请参阅 the Redux Style Guide