使用挂钩根据先前的状态值更新状态,我不理解 为什么 修改现有对象并将其传递给setState()是不好的。我知道这不会导致重新渲染,因为状态仍然指向相同的参考,但是除此之外,还有什么问题?我不明白如何克隆数组,对其进行修改,然后将其传递给setState()来解决一些未知问题。
const [bigArr, setBigArr] = setState(Array(SOME_BIG_NUMBER).fill(false));
// (1) This seems to work, but is bad for some reason. But why?
bigArr[325] = true;
setBigArr(bigArr);
// (2) This is preferable for some reason. Why?
bigArrCopy = bigArr.slice();
bigArrCopy[325] = true;
setBigArr(bigArrCopy);
// (3) Is this OK? Why/Why not?
setBigArr(bigArrCopy => {
bigArrCopy[325] = true;
return bigArrCopy;
});
答案 0 :(得分:1)
我知道这不会引起重新渲染,因为状态仍然指向相同的参考,但是除此之外,这又是什么问题?
还不够吗?设置状态的原因是因为您要重新渲染组件。如果您要重新渲染它,但没有,那是一个非常严重的错误。
react使用不可变状态模型的根本原因是,它使判断状态是否改变变得非常简单。在两个状态之间快速进行===
,您立即知道它是否已更改。如果您更改状态,则会丢失此功能,并且依赖此功能的所有代码都会中断。
答案 1 :(得分:0)
第一种情况不会按预期工作,它将不会重新渲染组件,因为React使用浅表比较,这意味着它将比较对象和数组的位置,如果位置不变,则React不会触发重新渲染< / p>
// (1) This will not re-render the component, cuz the location of bigArr are not changed
bigArr[325] = true;
setBigArr(bigArr);
与第三种情况相同
您可能会说可以通过致电setBigArr([...bigArr]);
来解决。但是仍然有问题
这是一个简单的情况,您会得到意想不到的结果
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [arr, setArr] = useState([1]);
const handleClick = () => {
createTimeout()
// Case1: directly set state
arr[0]= 2
setArr(arr);
// Case1: Not directly set state
// const newArr = [...arr]
// newArr[0]= 2
// setArr(newArr);
};
const createTimeout = () =>{
setTimeout(() =>{
// You expect number 1 here because you call this before update state
console.log(arr[0])
},2000)
}
return (
<div className="App">
<h1>{arr[0]}</h1>
<div onClick={handleClick}>change</div>
</div>
);
}
我们在createTimeout
之前致电setState
,因此我们希望记录数字1
,但是:
情况1:,因为您对原始数组进行了变异,您将获得数字2
情况2:,您将获得1号(预期)
答案 2 :(得分:0)
在重新渲染之前,进行检查以查看bigArrState!== prevBigArrState。它不检查数组的内容。它检查它是否是同一实例。在第一个示例中,这将导致false,并且组件将不会重新呈现。当您使用bigArr.slice()时,您正在创建一个全新的数组,因此bigArrState!== prevBigArrState的结果为true,从而允许组件重新呈现。
您的最后一个示例将引起问题,因为更新程序功能未传递bigArrCopy,而是传递了bigArrState(相同的实例)。
https://reactjs.org/docs/react-component.html#setstate
您可以执行以下操作,而不是在内存中创建并克隆整个克隆:
setBigArr([
...bigArr.slice(0, 325),
true,
...bigArr.slice(326),
]);