【问题标题】:Component won't update when a redux state change occurs发生 redux 状态更改时组件不会更新
【发布时间】:2020-05-01 11:00:39
【问题描述】:
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 没有运行。 请问有什么解决办法吗?

【问题讨论】:

    标签: javascript reactjs react-native redux react-redux


    【解决方案1】:

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

    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 持有对我们创建的新对象的引用,假设引用是 。现在让我们制作一个对象的副本。

    let obj1 = {a: 'b'};
    let obj2 = obj1;
    
    console.log(obj1);
    console.log(obj2);

    由于变量包含一个引用,因此实际分配obj2 的是与 相同的引用。

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

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

    运行下面的sn-p自己看看:

    let obj1 = {a: 'b'};
    let obj2 = obj1;
    
    obj2.a = 'c';
    
    console.log(obj1);
    console.log(obj2);

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

    最简单的方法是创建一个全新的对象,然后使用 spread syntax 将旧对象的值填充。

    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);

    【讨论】:

    • 谢谢你,写的这么清楚,希望下次再见。但是我不明白,array.filter返回一个新数组,不是更新原来的但是为什么item.quantity++在旧数组中真的增加了?
    • 我将更新我的答案,因为我的解释太长,无法发表评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    • 2019-12-29
    • 2020-11-17
    • 1970-01-01
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多