我需要修改我的状态,但不确定如何正确执行操作。
我在我的状态下的帐户属性如下:
{
"account":{
"id":7,
"categories":[
{
"id":7,
"products":[
{
"productId":54
}
]
},
{
"id":9,
"products":[
{
"productId":89
}
]
}
]
}
}
我的动作调度了以下内容:
dispatch({
type: Constants.MOVE_PRODUCT,
productId: 54,
sourceCategoryId: 7,
targetCategoryId: 9
});
现在我的减速器骨架是:
const initialState = {
account: null,
};
const accounts = (state = initialState, action) => {
switch (action.type) {
case Constants.MOVE_PRODUCT:
/*
action.productId
action.sourceCategoryId
action.targetCategoryId
*/
const sourceCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.sourceCategoryId; });
const sourceCategory = state.account.categories[sourceCategoryIndex];
const targetCategoryIndex = state.account.categories.findIndex((category) => { return category.id === action.targetCategoryId; });
const targetCategory = state.account.categories[targetCategoryIndex];
// ??
return {...state};
}
}
export default accounts;
我很困惑,如果我直接在switch块内部更新状态,那是错误的吗?
是否必须是单线更新,该突变是否就位?或者只要我在switch块中进行就可以了?
更新
从操作中,我需要从sourceCategoryId中删除productId并将其添加到帐户状态对象内部的targetCategoryId中。
答案 0 :(得分:1)
是的,您不应该在减速器中执行state.foo = 'bar'
。来自redux docs:
我们不会改变状态。我们使用
Object.assign()
创建一个副本。Object.assign(state, { visibilityFilter: action.filter })
也是错误的:它将改变第一个参数。您必须提供一个空对象作为第一个参数。您还可以启用对象散布运算符建议来改为编写{ ...state, ...newState }
。
所以减速器看起来像
function accountsReducer (state = initialState, { sourceCategoryId, productId }) {
const targetProduct = state.categories
.find(({ id }) => id === sourceCategoryId)
.products
.find(({ id }) => id === productId);
switch (action.type) {
case Constants.MOVE_PRODUCT:
return {
...state,
categories: state.categories.reduce((acc, cat) => {
return cat.id !== sourceCategoryId
? {
...acc,
cat: { ...cat, products: cat.products.filter(({ id }) => id !== productId) }
}
: {
...acc,
cat: { ...cat, products: [...cat.products, targetProduct] }
}
}, {});
};
}
}
但这很痛苦...您应该尝试normalize your data成平面阵列。
答案 1 :(得分:0)
// first, let's clean up the action a bit
// type and "payload". I like the data wrapped up in a bundle with a nice
// bow on it. ;) If you don't like this, just adjust the code below.
dispatch({
type: Constants.MOVE_PRODUCT,
payload: {
product: { productId: 54 }
sourceCategoryId: 7,
targetCategoryId: 9
}
});
// destructure to get our id and categories from state
const { id, categories } = state
// map the old categories to a new array
const adjustedCategories = categories.map(cat => {
// destructure from our payload
const { product, sourceCategoryId, targetCategoryId } = action.payload
// if the category is the "moving from" category, filter out the product
if (cat.id === sourceCategoryId) {
return { id: cat.id, products: [...cat.products.filter(p => p.productId !== product.productId)
}
// if the category is our "moving to" category, use the spread operator and add the product to the new array
if (cat.id === targetCategoryId) {
return { id: cat.id, products: [...cat.products, product] }
}
)
// construct our new state
return { id, categories: adjustedCategories }
此解决方案使函数保持纯净,应为您提供所需的内容。它未经测试,因此可能并不完美。
答案 2 :(得分:0)
您可以采用以下方法:
const accounts = (state = initialState, action) => {
switch (action.type) {
case Constants.MOVE_PRODUCT:
// Extract action parameters
const { productId, sourceCategoryId, targetCategoryId } = action
// Manually "deep clone" account state
const account = {
id : state.account.id,
categories : state.account.categories.map(category => ({
id : category.id,
products : category.products.map(product => ({ productId : product.productId })
}))
}
// Extract source and target categories
const sourceCategory = account.categories.find(category => category.id === sourceCategoryId);
const targetCategory = account.categories.find(category => category.id === targetCategoryId);
if(sourceCategory && targetCategory) {
// Find product index
const index = sourceCategory.products.findIndex(product => (product.productId === action.productId))
if(index !== -1) {
const product = sourceCategory.products[index]
// Remove product from source category
sourceCategory.products.splice(index, 1)
// Add product to target category
targetCategory.products.splice(index, 0, product)
}
}
return { account };
}
}
答案 3 :(得分:0)
这是一个丑陋的解决方案:)
const accounts = (state = initialState, action) => {
switch (action.type) {
case Constants.MOVE_PRODUCT:
const sourceCategoryIndex = state.account.categories.findIndex(
el => el.id === action.sourceCategoryId
);
const targetCategoryIndex = state.account.categories.findIndex(
el => el.id === action.targetCategoryId
);
const sourceCategory = state.account.categories.find(
el => el.id === action.sourceCategoryId
);
const targetCategory = state.account.categories.find(
el => el.id === action.targetCategoryId
);
const itemToMove = sourceCategory.products.find(
el => el.productId === action.productId
);
const newSourceCategory = {
...sourceCategory,
products: sourceCategory.products.filter(
el => el.productId !== action.productId
)
};
const newTargetCategory = {
...targetCategory,
products: [...targetCategory.products, itemToMove]
};
const newCategories = Object.assign([], state.account.categories, {
[sourceCategoryIndex]: newSourceCategory,
[targetCategoryIndex]: newTargetCategory
});
return { ...state, account: { ...state.account, categories: newCategories } };
}
};
Phew :)作为一个学习者,对我来说非常好:)但是,我喜欢@Daniel Lizik的方法,使用reduce
。
这是工作示例:
const action = {
productId: 54,
sourceCategoryId: 7,
targetCategoryId: 9,
}
const state = {
"account":{
"id":7,
"categories":[
{
"id":7,
"products":[
{
"productId":54,
},
{
"productId":67,
},
]
},
{
"id":9,
"products":[
{
"productId":89,
}
]
}
]
}
};
const sourceCategoryIndex = state.account.categories.findIndex( el => el.id === action.sourceCategoryId );
const targetCategoryIndex = state.account.categories.findIndex( el => el.id === action.targetCategoryId );
const sourceCategory = state.account.categories.find( el => el.id === action.sourceCategoryId );
const targetCategory = state.account.categories.find( el => el.id === action.targetCategoryId );
const itemToMove = sourceCategory.products.find( el => el.productId === action.productId );
const newSourceCategory = {...sourceCategory, products: sourceCategory.products.filter( el => el.productId !== action.productId ) };
const newTargetCategory = { ...targetCategory, products: [ ...targetCategory.products, itemToMove ] };
const newCategories = Object.assign([], state.account.categories, { [sourceCategoryIndex]: newSourceCategory,
[targetCategoryIndex]: newTargetCategory }
);
const newState = { ...state, account: { ...state.account, categories: newCategories } };
console.log( newState );