当 redux 状态发生变化时,组件不会更新

IT技术 javascript reactjs react-native redux react-redux
2021-03-26 07:31:09
case 'ADD_TO_CART': {
    let item = action.payload;
    let newState = addToCart(state, item);
    return newState;
}
const increaseItem = (array = [], dish) => {
    array.filter((item) => {
        if (item.id === dish.id) {
            item.quantity++;
        }
    });
    return array;
}

case 'INCREASE_ITEM': {
    let item = action.payload;
    console.log('run in increase')
    let newState = increaseItem(state, item);
    return newState;
}

这是代码。问题是当属性数量增加时,redux 认为状态没有变化,所以 mapStateToProps 没有运行。请问有什么解决办法吗?

1个回答

Arrayfilter返回一个新数组,它不会更新原始数组。直接返回或保存到变量中进行更改

const increaseItem = (array = [], dish) => {
    return array.filter(//your code here);
    //return array;
}

// OR

const increaseItem = (array = [], dish) => {
    const newArray = array.filter(//your code here);
    return newArray;
}

但是,这并没有按照您的想法行事。你应该map改用

const increaseItem = (array = [], dish) => {
    return array.map((item) => {
        if (item.id === dish.id) {
            item.quantity++;
        }
        return item; // Add this
    });
}

filter如果回调函数返回 true,则只会从数组中返回值。您的函数不检查它是否应该过滤,而是尝试修改值(并且是)。

map将为数组的每个索引返回回调的值。因此,如果您在最后返回每个项目,则您给定的回调应该按照您的预期执行,如上所示。

问题的最后一部分是确保您不会改变状态。这很可能是您问题的根源

const increaseItem = (array = [], dish) => {
    return array.map((item) => {
      let item = {...item}; // Add this
        if (item.id === dish.id) {
            item.quantity++;
        }
        return item;
    });
}

使用mapfilter,您正在创建一个新的状态数组。但是,在执行 时item.quantity++;,您正在改变原始状态和新状态中的嵌套对象,因为嵌套对象仍在使用相同的引用。在映射时创建新对象确保不仅主状态数组是新的,而且任何嵌套对象也是新的(这个特定的例子只保护 1 个深度)。

解释

它比答案更长,但我想说清楚。

您遇到的问题非常普遍,与 JavaScript 处理非原始数据类型的方式有关。当您创建一个数组或对象并将其分配给一个变量时,该变量实际上并不包含该对象,它包含一个引用或指向该对象的指针。对象本身实际上存储在内存中的其他地方。

为清楚起见,让我们只用被 包围的数字来表示引用<>让我们创建一个对象:

let obj1 = {a: 'b'};

obj1保存对我们创建的新对象的引用,假设引用是 <1>。现在让我们制作对象的副本。

let obj1 = {a: 'b'};
let obj2 = obj1;

console.log(obj1);
console.log(obj2);

由于变量持有一个引用,实际分配的obj2是与 <1> 相同的引用。

obj1 
// reference: <1>
// value: {a: 'b'}

obj2
// reference: <1>
// value: {a: 'b'}

所以误解出现在这里是因为人们认为obj2现在是它自己独立的原始副本。但是如您所见,它们引用了内存中的同一个对象。结果是,执行类似obj2.a = 'c'now 的obj1.a操作也会导致等于 'c'。

运行下面的代码片段亲自查看:

let obj1 = {a: 'b'};
let obj2 = obj1;

obj2.a = 'c';

console.log(obj1);
console.log(obj2);

我们如何避免制作误导性副本?

最简单的方法是创建一个全新的对象,然后使用扩展语法用旧对象的值填充它

let obj1 = {a: 'b'};
let obj2 = {...obj1};

// obj1 
// reference: <1>
// value: {a: 'b'}

// obj2
// reference: <2>
// value: {a: 'b'}

obj2.a = 'c';

console.log(obj1);
console.log(obj2);

现在您可以看到我们复制了一个对象,但每个对象在内存中都引用了自己的对象。这几乎总是我们想要的行为。

当我们引入嵌套时,事情会变得更加混乱。但如果你理解了基本思想,它应该更有意义。

let obj1 = {
  foo: 'bar',
  nested: {a: 'b'}
};

// obj1 
// reference: <1>
// value: {foo: 'bar', nested: <2>}

// nested
// reference: <2>
// value: {a: 'b'}

嵌套对象也有自己的引用。因此,当我们解构以创建新对象时,这就是我们正在做的事情。

let obj2 = {...obj1};

obj1 
// reference: <1>
// value: {foo: 'bar', nested: <2>}

nested
// reference: <2>
// value: {a: 'b'}

obj2
// reference: <3>
// value: {foo: 'bar', nested: <2>}

obj2 引用内存中的新位置,但嵌套对象仍然具有与以前相同的引用!

因此,如果我们修改嵌套属性,即使我们在顶部创建了一个新对象,我们也会有与以前类似的行为。这称为“浅拷贝”。试试看:

let obj1 = {
  foo: 'bar',
  nested: {a: 'b'}
};

let obj2 = {...obj1};

obj2.nested.a = 'c';

console.log(obj1);
console.log(obj2);

解决方案:也创建所有嵌套值的新对象。

let obj2 = {...obj1, nested: {...obj1.nested}};

现在我们已经成功地创建了一个完全独立的嵌套对象副本。

obj1 
// reference: <1>
// value: {foo: 'bar', nested: <2>}

nested
// reference: <2>
// value: {a: 'b'}

obj2
// reference: <3>
// value: {foo: 'bar', nested: <4>}

nested
// reference: <4>
// value: {a: 'b'}

您可以obj2放心地编辑及其嵌套值,obj1并且它的嵌套值将保持不变。

let obj1 = {foo: 'bar', nested: {a: 'b'}};
let obj2 = {...obj1, nested: {...obj1.nested}};

obj2.nested.a = 'c';

console.log(obj1);
console.log(obj2);

我将更新我的答案,因为我的解释太长而无法发表评论。
2021-06-18 07:31:09
谢谢你,这很清楚,希望下次再见。但我不明白,array.filter 返回一个新数组,而不是更新原始数组,但为什么 item.quantity++ 真的在旧数组中增加了?
2021-06-19 07:31:09