React:在什么条件下 useState 钩子中的 setState 会导致重新渲染?

Vam*_*iro 6 rerender reactjs react-hooks

我有以下代码(CodeSandbox):

function App() {
  const [blah, setBlah] = useState(true);
  console.log('BLAH', blah);
  setBlah(true);

  return <button onClick={() => setBlah(true)}>Blah</button>;
}
Run Code Online (Sandbox Code Playgroud)

据我所知,setBlah(true)组件的顶层导致了太多的重新渲染。我不明白的是,如果您注释掉顶层setBlah(true)并开始混合“Blah”按钮,为什么该组件不会重新渲染?两者都一遍又一遍地将 的状态设置blah为 true,但只有顶层setBlah(true)会导致重新渲染。

来自React 文档关于退出状态更新:“请注意,React 可能仍需要在退出之前再次渲染该特定组件。” 这里的关键词是“可能”,对我来说这意味着根据文档我的情况是有效的。所以问题可能会变成,在什么条件下setState,使用已设置的相同值调用 会导致重新渲染?了解这一点很有用,因为人们可能希望在setState方法前面放置逻辑来检查该值是否与当前值相同,以免导致错误的重新渲染。

jse*_*ksn 4

通过您在问题中链接到的文档:

\n
\n

如果将 State Hook 更新为与当前状态相同的值,React 将退出而不渲染子项或触发效果。(React 使用Object.is比较算法。)

\n
\n

您可以在那里阅读 ReactObject.is在比较先前和新状态值时使用的内容。如果比较的返回值为true,则 React 不会使用该setState调用来考虑是否重新渲染。true这就是为什么在处理程序中设置状态值onClick不会导致重新渲染。

\n

也就是说,无条件地调用setState任何组件顶层的函数始终是一个错误,因为它会(无限地)启动协调算法。在我看来,这就是你问题的核心,如果你想了解React Fiber(React核心算法的实现),那么你可以从这里开始: https: //github.com/acdlite/反应纤维架构

\n

这是 React 文档中的另一条注释(需要针对功能组件进行更新):

\n
\n

可以setState()立即调用,componentDidUpdate()但请注意,它必须包含在如上例所示的条件中,否则\xe2\x80\x99将导致无限循环。

\n

\xe2\x86\xb3 https://reactjs.org/docs/react-component.html#componentdidupdate

\n
\n

解释类组件生命周期方法如何转换为函数组件超出了本问题的范围(您可以在 Stack Overflow 上找到解决此问题的其他问题和答案);但是,该指令适用于您的情况。

\n

这是一个片段,显示您的组件仅在错误setState调用被删除时呈现一次:

\n

\r\n
\r\n
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>\n<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>\n<script src="https://unpkg.com/@babel/standalone@7.16.3/babel.min.js"></script>\n\n<div id="root"></div>\n\n<script type="text/babel" data-type="module" data-presets="react">\n\nconst {useRef, useState} = React;\n\nfunction Example () {\n  const renderCountRef = useRef(0);\n  renderCountRef.current += 1;\n\n  const [bool, setBool] = useState(true);\n\n  return (\n    <div>\n      <div>Render count: {renderCountRef.current}</div>\n      <button onClick={() => setBool(true)}>{String(bool)}</button>\n    </div>\n  );\n}\n\nReactDOM.render(<Example />, document.getElementById(\'root\'));\n\n</script>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n