反应挂钩以根据先前的状态值更新状态

时间:2020-07-08 00:32:10

标签: javascript reactjs react-hooks use-state

使用挂钩根据先前的状态值更新状态,我不理解 为什么 修改现有对象并将其传递给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;
});

3 个答案:

答案 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号(预期)

Check out the codesandbox

答案 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),
]);