使用hooks设置相同值的状态会导致重新渲染吗?

32

使用Hooks,如果我使用与 state 相同的值调用 setState,它会重新渲染组件吗?

如果是,我该如何避免这种情况?

例如:

const [state, setState] = useState(foo)

...
// any where in the code
setState(foo)

考虑到foo可以是任何东西,比如{}trueprops.bar或者来自组件外部(常量)的变量。

6个回答

25

如果您使用相同的值调用setState,它将不会重新渲染组件。

尝试一下:

import React, { useState, useEffect } from "react";

const foo = { foo: 'bar' };

export default ({ name }) => {
  const [state, setState] = useState(foo);
  console.log("rendered!");
  useEffect(() => {
    setState(foo);
    console.log("state reset!");
  });

  const handleClick = () => {
    console.log("handleClick!");
    setState(foo);
    // setState({ ...foo, bar : 'baz' });
  }

  return (<div>
  <h1>Hello {name}!</h1>
  <button onClick={handleClick}>Click Me</button>
  </div>);
};

即使按钮被点击,但是由于值没有改变,组件并不会重新渲染。如果你改变了调用 setState 的参数,那么它将会重新渲染组件。


这里是一个 代码示例 供您参考。

尝试注释掉 handleClick 方法中的第一个 setState 并取消注释第二个 setState,以查看差异。


1
我认为,它并不是重新渲染,而是由虚拟DOM重新评估,只有在有更改时才会在真实的DOM中发生重新渲染。 - Parviz Pirizade
3
只是想再添加一个参考来确认这一点。React Hooks文档提到: 如果你的更新函数返回与当前状态完全相同的值,则随后的重新渲染将被完全跳过。 https://reactjs.org/docs/hooks-reference.html#functional-updates - Joyce Lee
6
在上面的例子中,需要添加一条注释。如果您使用像setState({ foo: 'bar' })这样的字面对象,即使该对象看起来相同,它也会被重新呈现。这是因为每次创建字面对象时,它都被视为与旧对象不同。就像比较两个字面对象{a: 1} === {a: 1}时返回false一样。 - Cuong Vu

16

2
我认为我们不应该区分。如果它是相同的(相同的引用)对象或数组,无论是因为它在渲染函数外声明还是通过useMemo声明,它都不会触发重新渲染。因此,我们可以简单地说“如果Object.is(prevValue, newValue)返回true,它就不会重新渲染”。 - Ricola
除此之外,据我所知,React 在依赖数组(useMemo、useEffect 等)中使用 Object.is() 方法来比较值。我猜它也会在你将值作为参数传递给 setState 函数以及当前状态时使用该方法进行比较。 - Vlad R

3

有点晚了,但我发现一种奇怪的情况,即将状态设置为相同的值会触发两次重新渲染,然后通常会表现出良好的行为。将状态设置为2将触发两次重新渲染。

任何解释?

export default function App() {
  const [state, setState] = React.useState(1);
  console.log("App rendered");
  console.log({ state });

  return (
    <div className="App">
      <button
        onClick={() => {
          setState(1);
        }}
      >
        update state with same value
      </button>
      <button
        onClick={() => {
          setState(2);
        }}
      >
        update state with different value
      </button>
      <div>state:{state}</div>
    </div>
  );
}

我原以为是Strict Mode的双重渲染导致的,但看起来情况并非如此:https://codesandbox.io/s/serene-lake-h34byu?file=/src/App.js:127-128。 很奇怪,当你点击另一个按钮两次时,它允许两次渲染,然后如果你再次点击第一个按钮,它将再次允许两次渲染。 老实说,我不确定为什么,看起来像是React的内部实现细节? - Lisenish

3

这是一个与js语法有关的问题,就像===操作一样。

let times = 0
const init = {name: 'Bob'}
function App() {
  const [state, setState] = useState(init)
  function modify() {
    setState({name: 'Bob'})
  }
  function modify2() {
    setState(init)
  }
  times ++
  return (
    <div className="App">
      <p>{ times }</p>
      <button onClick={modify}>Same Value Will Rerender</button>
      <button onClick={modify2}>Same Reference Never Rerender</button>
    </div>
  );
}

这里有一个代码沙盒链接:https://codesandbox.io/s/morning-haze-6ttm0。您可以重新编写一个包装方法:
let times = 0
const init = {name: 'Bob'}
function App() {
  const [state, setState] = useState(init)
  function modify3() {
    setState2({ name: 'Bob' })
  }
  function setState2(value) {
    if (value.name === state.name) {
      return
    }
    setState(value)
  }
  times ++
  return (
    <div className="App">
      <p>{ times }</p>
      <button onClick={modify3}>Same Value Will Not Rerender Yet</button>
    </div>
  );
}

1
如果setState函数或者useReducer Hook返回的值与当前状态相同,React将会放弃渲染子组件或触发副作用,因为它使用了Object.is比较算法。

0

1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。-【来自审查】 - pitamer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接