如何更新状态数组中的对象属性

How to update object properties in state array

所以,我正在学习 React 课程。第一次学习这个框架,所以这可能是一个愚蠢的问题。这是我得到的代码,用于更新存储在状态中作为对象数组的对象的 属性。

const [squares, setSquares] = React.useState(boxes)

function toggle(clickedSquare) {
    setSquares(prevSquares => {
        return prevSquares.map((square) => {
            return square === clickedSquare ? {...square, on: !square.on} : square
        })
    })
}

...但是,我编写的以下代码也可以工作并且看起来更简单,这种方法有什么问题?状态值只是浅层不可变的。据我所知,存储在状态数组中的对象本身是可变的...

const [squares, setSquares] = React.useState(boxes)

function toggle(clickedSquare) {
    clickedSquare.on = !clickedSquare.on;
    setSquares(prevSquares => [...prevSquares])
}

还要考虑这个示例,其中我有一个数组,其中包含保存在状态中的深层嵌套对象。

[
    {
        trunk: {
            limb: {
                branch: {
                    twig: {
                        leaf: {
                            color: "green"
                        }
                    }
                }
            }
        }
    }
]

我想把那个“绿色”改成“棕色”。它在本文 Handling State in React 中指出...

  1. Deep cloning is expensive
  2. Deep cloning is typically wasteful (instead, only clone what has actually changed)
  3. Deep cloning causes unnecessary renders since React thinks everything has changed when in fact perhaps only a specific child object has changed.

树示例中发生变化的只是叶对象。所以只有那个需要被克隆,而不是数组,而不是整个树或树干对象。这对我来说更有意义。有人不同意吗?

这仍然留下(没有双关语意)通过更新状态数组中的 属性 值而不是克隆具有更改的对象会引入哪些错误的问题?一个具体的例子会非常好,这样我就可以更好地理解我可以优化性能的地方。

clickedSquare.on = !clickedSquare.on;是状态突变。不要改变 React 状态。

以下代码可能有效的原因是因为它浅复制了 squares 状态数组,这会触发重新渲染并公开变异的数组元素。

function toggle(clickedSquare) {
  clickedSquare.on = !clickedSquare.on;        // <-- mutation!
  setSquares(prevSquares => [...prevSquares]); // new array for Reconciliation
}

在这种特定情况下,它可能不会产生任何不利影响,但变异状态是一把好脚枪,可能会给 debug/diagnose 带来潜在的困难错误,尤其是如果它们的影响要等到几个 children ReactTree 的更深处。

总是使用第一种方法并应用不可变更新模式。当更新 React 状态的任何部分时,即使是嵌套状态,也需要创建新数组和 object 引用才能使 React 的协调过程正常工作。

function toggle(clickedSquare) {
  setSquares(prevSquares => prevSquares.map((square) => // <-- new array
    square === clickedSquare
      ? { ...square, on: !square.on } // <-- new object
      : square
  ));
}

在这里,您使用的是 map() 方法,该方法 return 对于该条件只有真或假。 因此,您应该使用 filter() 方法而不是 map() 方法,后者 return 针对该条件过滤数据。

例如:

const arr = [
    {
        name: 'yes',
        age: 45
    },
    {
        nmae: 'no',
        age: 15
    }
]

const filterByMap = arr.map(elm => elm.age > 18)
console.log(filterByMap) // outputs --> [ true, false ]

const filterByFilter = arr.filter(elm => elm.age > 18)
console.log(filterByFilter) // outputs --> [ { name: 'yes', age: 45 } ]