为什么 useState 不会触发重新渲染?

IT技术 javascript reactjs react-hooks
2021-01-25 16:02:37

我已经初始化了一个数组状态,当我更新它时,我的组件不会重新渲染。这是一个最小的概念验证:

function App() {
  const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
  console.log("rendering...");
  return (
    <div className="App">
      {numbers.map(number => (
        <p>{number}</p>
      ))}
      <input
        type="text"
        value={numbers[0].toString()}
        onChange={newText => {
          let old = numbers;
          old[0] = 1;
          setNumbers(old);
        }}
      />
    </div>
  );
}

根据这段代码,输入似乎应该包含数字 0 开始,并且任何时候它改变,状态也应该改变。在输入中输入“02”后,App组件不会重新渲染。但是,如果我在 5 秒后执行的 onChange 函数中添加 setTimeout,则表明数字确实已更新。

关于为什么组件不更新的任何想法?

这是一个带有概念证明的CodeSandbox

6个回答

您正在调用setNumbers并将其传递给它已有的数组。你已经改变了它的一个值,但它仍然是同一个数组,我怀疑 React 没有看到任何重新渲染的理由,因为状态没有改变;新数组是旧数组。

避免这种情况的一种简单方法是将数组扩展到新数组中:

setNumbers([...old])
此解决方案是否有任何性能影响?
2021-03-16 16:02:37
ohhhh 所以它在内部比较引用而不是检查完全相等。说得通。如果我们可以决定自己重新渲染而不是让状态设置器在后台执行,那就太好了(也许我已经不知道了)
2021-03-23 16:02:37
子组件呢?
2021-03-23 16:02:37
很好的答案。我在 object.likes 之后进行排序,并且 setRecipes([...recipes.sort((a, b) => (b.likes.length - a.likes.length))]) 触发了重新渲染,一切都像魅力一样。谢谢。
2021-03-30 16:02:37
这是最好的答案。谢谢!
2021-04-01 16:02:37

你需要numbers像这样复制let old = [...numbers];

useState仅当值已更改时才更新该值,因此如果它已更改44并变为7它,它将更新。但是它如何知道数组或对象是否已更改。它是通过引用,所以当你这样做时,let old = numbers你只是传递一个引用而不是创建一个新的

其他人已经给出了技术方案。对于为什么会发生这种情况感到困惑的任何人,是因为 setSomething() 仅当且仅当先前和当前状态不同时才重新呈现组件。由于 javascript 中的数组是引用类型,如果你在 js 中编辑一个数组的某一项,它仍然不会改变对原始数组的引用。在 js 眼中,这两个数组是一样的,尽管这两个数组里面的原始内容是不同的。这就是 setSomething() 无法检测到对旧数组所做更改的原因。

请注意,如果您使用类组件并使用 setState() 更新状态,那么无论状态是否已更改,组件都将始终更新。因此,您可以将功能组件更改为类组件作为解决方案。或者按照其他人提供的答案进行操作。

你可以像这样改变状态

const [state, setState] = ({})
setState({...state})

或者如果你的状态是 Array 你可以像这样改变

const [state, setState] = ([])
setState([...state])
对我不起作用
2021-03-30 16:02:37
这对我有用!!你能解释一下为什么会这样吗?
2021-04-06 16:02:37
  //define state using useState hook
  const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);

  //copy existing numbers in temp
  let tempNumbers = [...numbers];
  // modify/add no
  tempNumbers.push(4);
  tempNumbers[0] = 10;
  // set modified numbers
  setNumbers(tempNumbers);
不要简单地发布一段代码。而是尝试提供更多上下文/解释。
2021-03-18 16:02:37