Reactjs 当我必须遍历和查找/删除状态中的项时,如何安全地更新状态
我需要修改我的状态,但我不确定如何正确执行 我所在州的帐户属性如下所示:Reactjs 当我必须遍历和查找/删除状态中的项时,如何安全地更新状态,reactjs,react-redux,Reactjs,React Redux,我需要修改我的状态,但我不确定如何正确执行 我所在州的帐户属性如下所示: { "account":{ "id":7, "categories":[ { "id":7, "products":[ { "productId":54
{
"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;
我很困惑,如果我直接在开关块内部更新状态,这是错误的吗
它是否必须是一个线性更新,在适当的地方进行突变,或者只要我在开关块中进行突变,就可以了
更新
在操作中,我需要从sourceCategoryId中删除productId,并将其添加到account state对象内部的targetCategoryId中
这个解决方案保持了函数的纯粹性,应该满足您的需要。它没有经过测试,所以可能并不完美 您可以采取以下方法:
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 };
}
}
是的,您不应该在减速机中执行
state.foo='bar'
。从:
我们不会改变国家。我们使用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] }
}
}, {});
};
}
}
但这是一种痛苦……您应该尝试将其放入平面阵列中。这是一个丑陋的解决方案:) Phew:)作为一名学习者,这对我很好:)但是,我喜欢@Daniel Lizik的方法,使用
reduce
以下是工作示例:
const action={
产品编号:54,
资料来源类别ID:7,
目标类别ID:9,
}
常量状态={
“帐户”:{
“id”:7,
“类别”:[
{
“id”:7,
“产品”:[
{
“productId”:54,
},
{
“productId”:67,
},
]
},
{
“id”:9,
“产品”:[
{
“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.targetCategorid);
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,{[SourceCategorinIndex]:NewSourceCategority,
[targetCategoryIndex]:新目标类别}
);
const newState={…state,account:{…state.account,categories:newCategories};
console.log(newState)代码>不应直接更新状态或对其进行变异。除非你这样做,否则你可以在你的案件中做任何事情。您想如何更新您的状态?我看不懂你的行为,也许我不清楚。但是,如果你提供更多的细节,这将是有用的。移动产品,但移动的产品(来源)会发生什么?删除产品或其他什么东西?@ DeSerCKIN是的,从SoeCeCeCordRyID中删除并添加到TaleCaseRyIDID。考虑规范化您的状态以看起来更像关系数据库,它将消除遍历状态树(因为它现在是平的)的需要。是的,作为其当前形状,很难更新它。我正在研究它,我将提供一个解决方案,但它非常难看:)感谢那个规范化链接,我将研究它。顺便说一句,我如何在我当前的switch语句中使用这个reducer?初始状态没有类别,它有帐户。所以我们需要另一个关卡,对吗?在“返回”中{…state,state.account.categories:``part?但这不会返回帐户。在调用createStore
的地方发布代码。在我看来,你应该在初始状态中实例化一个空的帐户。categories
数组,这样你就不必检查数组是否存在。
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] }
}
}, {});
};
}
}
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 } };
}
};