如果对象引用未更改,则 useState 正在接受更新

useState is accepting updates if the object reference does not change

我认为 useState 需要一个新值才能接受更新:

我创建了这个codesandbox 来说明我的意思。

我有一个这样的嵌套数据结构

   const state = {
      name: "parent",
      data: {
        isExpanded: false,
        children: [
          { name: "one", data: { isExpanded: false } },
          { name: "two", data: { isExpanded: false } },
          { name: "three", data: { isExpanded: false } }
        ]
      }
    };

然后我使用 useState 在状态改变时引起重新渲染:

function App() {
  const [tree, setTree] = useState(state);

  const toggleExpanded = node => {
    node.data.isExpanded = !node.data.isExpanded;

    setTree(tree);
  };

  return (
    <div className="App">
      <h1>IsExpanded</h1>
      {tree.data.children.map(child => (
        <button onClick={() => toggleExpanded(child)}>
          {child.name} - {child.data.isExpanded ? "on" : "off"}
        </button>
      ))}
    </div>
  );
}

我每次调用 setTree 时都使用相同的引用并且正在进行更新。

我认为每次都需要一个新的引用才能发生这种情况?

我可能遗漏了一些东西,但更新没有发生在同一个参考文献上。

最好使用不可变状态,因为这种方法是 React 惯用的。但这也适用于改变现有状态。 setTree(tree)触发更新,tree相同或不同无关紧要。

setState(sameState)在class组件中的作用相同,与forceUpdate类似,一般不提倡。

这是预期的行为。 useState 返回的 setter 在这方面与非钩子 setState 的工作方式相同。它的任何执行都会触发重新渲染,并且它是否是相同的对象引用并不重要。

如果你刚刚做了:

node.data.isExpanded = !node.data.isExpanded;

如果不调用 setTree,则不会发生视觉更新,因为不会触发重新渲染。

通常建议避免通过更改现有状态对象来进行状态更新,因为这可能会导致某些类型的错误(例如,如果您有任何将先前状态与新状态进行比较的逻辑,它们似乎总是如果您通过改变现有对象进行更改,则相同)。这可能导致的微妙问题可能会随着未来的 React 并发模式功能而扩展,但 React 不会阻止您这样做。