为什么 React 调用我的组件的次数超出了需要的次数?

问题描述 投票:0回答:3

我有一个简单的组件示例,它使用

useRef()
来计算组件渲染的次数,并使用
useState()
来维护某些状态:

const {useState, useRef} = React;
const App = () => {
  const [counter, setCounter] = useState(0);
  const renderCount = useRef(0);
  const onClick = () => {
    setCounter(1);
  }
  renderCount.current++;
  alert("rendering"); // executes 3 times, not twice.
  return <div>
    <p>Render count: {renderCount.current}</p>
    <button onClick={onClick}>{counter}</button>
  </div>
}

ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>

当我运行上面的代码片段时,

alert
会触发,并且引用计数器由于第一次渲染而增加。当我第一次单击该按钮时,会调用
setCounter(1)
,这会导致组件重新渲染/重新执行(如预期),从而导致警报第二次出现并且引用计数器增加。当我第二次单击该按钮时,
setCounter(1)
运行(它不会更新状态,因为它已经是
1
)。由于状态没有改变,我希望不需要重新渲染,因此我的
App
组件不会再次执行。但是,我看到了第三个警报,表明该组件已再次执行,但引用计数器没有增加,第二次单击后渲染的输出也没有更改。进一步单击该按钮不会按预期显示任何警报。我的问题是,当状态没有改变时(即:第二次单击按钮时),为什么 React 会再次执行我的 App 组件?

javascript reactjs setstate
3个回答
2
投票

这是React重新渲染的渲染行为,这是一个特例。如果将状态更新为与当前状态相同的值,则 React 会再次重新渲染组件并退出后续重新渲染。

  • 第一个渲染是初始渲染,state为0
  • 第二次渲染用于状态更改state为1
  • 第三个渲染是针对特殊情况的(这不是初始渲染)state是1

然而,这种不必要的重新渲染并不是初始渲染的情况。我的意思是,如果您在

setCounter(0)
方法中使用
onClick
而不是
setCounter(1)
,那么即使随后调用
onClick
方法,组件也只会渲染一次。

流程如下图所示:

原因是; React 无法猜测渲染的输出不会改变,它必须再渲染一次并将结果与上一次渲染的结果进行比较。但我不确定它在初始状态下是如何处理的(也许是一个标志)。文档中没有太多关于它的信息。

你可能认为存在效率问题,但实际上并不存在。 React 渲染周期有两个阶段:渲染阶段提交阶段。 DOM 在提交阶段后更新,在这种情况下,进程不会进入提交阶段,并且不会导致 DOM 更新


0
投票
你没有使用React的钩子

useEffect()

,这就是为什么你的函数,随时调用,调用它的本地作用域函数。将您的函数组件视为一个字母。您将其交给信函关闭并签名。但在你的情况下,你传递了它打开(在传输过程中它被读取和调用)但签名(它知道它会在哪里),因此你需要使用钩子/生命周期方法来控制你的React组件。

这就是您遇到警报和增量的原因。

如果您已经使用过 React 类组件,那么您应该知道它的生命周期是什么。如果没有,我鼓励您阅读这篇文章:

React Lifecycles

useEffect 具有 componentDidMount

componentDidUpdate
componentWillUnmount
 之间的连接行为。每当您更新状态或道具时,都会调用此方法并执行给定的代码。


0
投票
React 文档(Hooks API 参考)阐述了 bailing 的概念:

摆脱状态更新

如果将 State Hook 更新为与当前状态相同的值,React 将在不渲染子级或触发效果的情况下退出。

请注意,React 可能仍需要在退出之前再次渲染该特定组件。

因此,在您的情况下,由于

alert("rendering");

 位于组件主体中,因此在 React“跳出”之前它仍然会被触发。

© www.soinside.com 2019 - 2024. All rights reserved.