简体   繁体   English

为什么在 state 更改后我的组件不会自动更新?

[英]Why does my component not auto update after a state change?

I am following a tutorial on Scrimba for React and currently doing the Boxes challenge part 4.我正在关注关于 Scrimba for React 的教程,目前正在做 Boxes 挑战第 4 部分。

I pretty much have a function that takes in an ID, and that ID relates to an array of key-value pairs.我几乎有一个 function 接受一个 ID,并且该 ID 与键值对数组相关。

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

function toggle(id) {

        /**
         * Challenge: use setSquares to update the
         * correct square in the array.
         * 
         * Make sure not to directly modify state!
         * 
         * Hint: look back at the lesson on updating arrays
         * in state if you need a reminder on how to do this
         */

        setSquares(oldSquares => {
            let newSquares = oldSquares
            newSquares[id - 1].on = !oldSquares[id - 1].on
            return newSquares
        })
    }

Here is what squares contains:以下是 squares 包含的内容:

[{id: 1, on: true}, {id: 2, on: false}, {id: 3, on: true}, {id: 4, on: true}, {id: 5, on: false}, {id: 6, on: false}]

My function above properly inverts the value for the on key and I prove this through print statements, however my component displaying the squares does not update even though the state is updating.我上面的 function 正确反转了 on 键的值,我通过打印语句证明了这一点,但是即使 state 正在更新,我显示方块的组件也不会更新。

Anyone know why?有谁知道为什么?

You're not creating a new array in state, but mutating the existing one.您不是在 state 中创建新数组,而是对现有数组进行变异。 So the framework doesn't know there's a state update.所以框架不知道有 state 更新。

This creates a reference to the same array in memory:这会在 memory 中创建对同一数组的引用:

let newSquares = oldSquares

This mutates a value in that array:这会改变该数组中的一个值:

newSquares[id - 1].on = !oldSquares[id - 1].on

And this returns to state the same reference to the same array:这将返回到 state 对同一数组的相同引用:

return newSquares

Don't mutate state.不要突变 state。 Instead, create a new array with the updated elements:相反,使用更新的元素创建一个新数组:

setSquares(oldSquares => 
  oldSquares.map((s, i) =>
    i === id - 1 ?
      { ...s, on: !s.on } :
      s
  )
)

In this case .map() returns a new array reference, and within the .map() callback we conditionally return a new object with the on property negated if the index equals id - 1 or return the object as-is for all other indices.在这种情况下.map()返回一个新的数组引用,并且在.map()回调中,我们有条件地返回一个新on object 如果索引等于id - 1则该属性被否定,或者为所有其他索引返回 object .


For a much more verbose version to perhaps better illustrate the logic, it's effectively the same result as this:对于一个更详细的版本可能更好地说明逻辑,它实际上与以下结果相同:

setSquares(oldSquares => {
  let newSquares = oldSquares.map((s, i) => {
    if (i === id - 1) {
      return { ...s, on: !s.on };
    } else {
      return s;
    }
  });
  return newSquares;
})

Your problem is that React doesn't know you've updated your state.您的问题是 React 不知道您已经更新了 state。 You do你做

let newSquares = oldSquares

and then mutate newSquares (or rather one object inside it), but that variable still holds a reference to the same array that's your current state.然后变异newSquares (或者更确切地说是其中的一个object),但该变量仍然包含对与当前state相同的数组的引用。 React can't "see" that you've changed it. React 不能“看到”你已经改变了它。

Instead you need to create a new array reference with the values you need.相反,您需要使用所需的值创建一个新的数组引用。 It would probably work to replace the line quoted above with the following, which creates a shallow copy and therefore a new reference:将上面引用的行替换为以下内容可能会起作用,这会创建一个浅拷贝,因此会创建一个新的引用:

let newSquares = [...oldSquares]

But this still makes me nerve because you're updating an object contained within the array, and the above is only taking a shallow copy - so you're still actually mutating your current state.但这仍然让我很紧张,因为您正在更新包含在数组中的 object,而以上只是一个浅拷贝 - 所以您实际上仍在改变您当前的 state。 And doing this in React is always liable to introduce subtle bugs.在 React 中这样做总是容易引入细微的错误。

So better here is to clone more deeply.所以这里更好的是克隆得更深。 Since you're only updating a "top level" property of each object in the array, mapping a shallow clone operation over the array would be my choice:由于您只更新数组中每个 object 的“顶级”属性,因此我选择在数组上映射浅克隆操作:

let newSquares = oldSquares.map(obj => ({...obj}));

And actually, while you're doing this, you could get the update you want on the same line, meaning your state update function could be a single line实际上,当您执行此操作时,您可以在同一行获得所需的更新,这意味着您的 state 更新 function 可能是一行

return oldSquares.map(({on,...rest}, index) => (index === is - 1 ? {on:!on, ...rest} : {on,...rest}));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM