ChatGPT解决这个技术问题 Extra ChatGPT

ReactJS - 每次调用“setState”时都会调用渲染吗?

每次调用 setState() 时,React 是否会重新渲染所有组件和子组件?

如果是这样,为什么?我认为这个想法是 React 只在需要时渲染 - 当状态改变时。

在以下简单示例中,两个类在单击文本时再次呈现,尽管状态在后续单击时不会改变,因为 onClick 处理程序始终将 state 设置为相同的值:

this.setState({'test':'me'});

我原以为只有在 state 数据发生更改时才会发生渲染。

以下是示例 as a JS Fiddle 和嵌入代码段的代码:

var TimeInChild = React.createClass({ render: function() { var t = new Date().getTime(); return (

Time in child:{t}

); } }); var Main = React.createClass({ onTest: function() { this.setState({'test':'me'}); }, render: function() { var currentTime = new Date().getTime(); return (

Time in main:{currentTime}

点我更新时间

); } }) ; ReactDOM.render(
, document.body);

我有同样的问题,我不知道确切的解决方案。但是我已经从它开始照常工作的组件中清除了不需要的代码。
我想指出,在您的示例中 - 因为您的元素的设计方式不仅仅依赖于独特的状态 - 即使使用虚拟数据调用 setState() 也会导致元素呈现不同,所以我会说是的。当某些事情可能发生变化时,它绝对应该尝试重新渲染您的对象,否则您的演示 - 假设它是预期的行为 - 将无法工作!
你可能是对的@TadhgMcDonald-Jensen - 但据我了解,React 会第一次渲染它(因为状态从无变为第一次),然后再也不需要渲染。当然,我错了——因为看起来 React 要求你编写自己的 shouldComponentUpdate 方法,我假设它的简单版本必须已经包含在 React 本身中。听起来像 react 中包含的默认版本只是返回 true - 这会强制组件每次重新渲染。
是的,但它只需要在虚拟 DOM 中重新渲染,然后如果组件以不同的方式渲染,它只会更改实际的 DOM。对虚拟 DOM 的更新通常可以忽略不计(至少与修改实际屏幕上的内容相比),因此每次可能需要更新时调用渲染然后看到没有发生任何更改,这并不比假设它应该渲染相同的成本和安全性更高。

A
Ankit

每次调用 setState 时,React 是否会重新渲染所有组件和子组件?

默认情况下 - 是的。

有一个方法 boolean shouldComponentUpdate(object nextProps, object nextState),每个组件都有这个方法,它负责确定“应该组件更新(运行渲染函数)吗?”每次更改状态或从父组件传递新道具时。

您可以为您的组件编写自己的 shouldComponentUpdate 方法实现,但默认实现始终返回 true - 意味着始终重新运行渲染函数。

引用官方文档http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

默认情况下,shouldComponentUpdate 总是返回 true 以防止在状态发生变化时出现细微的错误,但是如果你小心地始终将状态视为不可变的并且在 render() 中从 props 和 state 中读取为只读,那么你可以覆盖 shouldComponentUpdate 与将旧道具和状态与其替代品进行比较的实现。

你问题的下一部分:

如果是这样,为什么?我认为这个想法是 React 只在需要时渲染 - 当状态改变时。

我们可以称之为“渲染”的有两个步骤:

虚拟 DOM 渲染:当 render 方法被调用时,它返回组件的一个新的虚拟 dom 结构。正如我之前提到的,这个渲染方法总是在你调用 setState() 时被调用,因为 shouldComponentUpdate 总是默认返回 true。因此,默认情况下,React 中没有优化。原生 DOM 渲染:React 仅在虚拟 DOM 中更改真实 DOM 节点时才会更改浏览器中的真实 DOM 节点,并且只需少量更改 - 这是 React 的伟大功能,它优化了真实 DOM 突变并使 React 快速运行。


很好的解释!在这种情况下,真正让 React 仍然大放异彩的一件事是,除非虚拟 DOM 已更改,否则它不会更改真实 DOM。因此,如果我的渲染函数连续 2 次返回相同的内容,那么真正的 DOM 根本不会改变……谢谢!
我认为@tungd 是对的 - 请参阅下面的答案。这也是组件之间的父子关系的问题。例如,如果 TimeInChild 是 Main 的兄弟,那么它的 render() 将不会被不必要地调用。
如果您的状态包含一个相对较大的 javascript 对象(总共约 1k 个属性),它被呈现为一个大的组件树(总共约 100 个)...您应该让渲染函数构造虚拟 dom,还是应该在设置之前状态,手动将新状态与旧状态进行比较,如果检测到有差异,则仅调用 setState?如果是这样,如何做到最好 - 比较 json 字符串,构造和比较对象哈希,...?
@Petr,但是即使 react 重构了虚拟 dom,如果旧的虚拟 dom 与新的虚拟 dom 相同,浏览器 dom 也不会被触及,不是吗?
此外,请考虑使用 React.PureComponent (reactjs.org/docs/react-api.html#reactpurecomponent)。只有当组件的状态或道具实际发生变化时,它才会更新(重新渲染)。但是请注意,这种比较是肤浅的。
A
Ankit

不,当状态发生变化时,React 不会渲染所有内容。

每当一个组件变脏(其状态改变)时,该组件及其子组件都会重新渲染。这在某种程度上是为了尽可能少地重新渲染。唯一不调用 render 的时候是某个分支移动到另一个根目录时,理论上我们不需要重新渲染任何东西。在您的示例中, TimeInChild 是 Main 的子组件,因此当 Main 的状态发生变化时,它也会重新呈现。

React 不比较状态数据。当调用 setState 时,它会将组件标记为脏(这意味着它需要重新渲染)。需要注意的重要一点是,尽管调用了组件的 render 方法,但只有在输出与当前 DOM 树不同时才会更新真实 DOM(也就是虚拟 DOM 树和文档的 DOM 树之间的差异)。在您的示例中,即使状态数据没有更改,但上次更改的时间确实发生了变化,这使得 Virtual DOM 与文档的 DOM 不同,因此 HTML 会更新。


是的,这是正确的答案。作为实验,将最后一行修改为 React.renderComponent(
, document.body);并从 Main 组件的 render() 主体中删除 。 TimeInChild 的 render() 默认不会被调用,因为它不再是 Main 的子级。
谢谢,像这样的东西有点棘手,这就是为什么 React 作者建议 render() 方法应该是“纯”的——独立于外部状态。
@tungd,some branch is moved to another root 是什么意思?你怎么称呼branch?你怎么称呼root
我真的更喜欢标记为正确的答案。我理解您对原生“真实”方面的“渲染”的解释......但是如果您从 react-native 方面来看它,您必须说它再次渲染。幸运的是,它足够聪明,可以确定真正发生了什么变化并只更新这些东西。这个答案可能会让新用户感到困惑,首先你说不,然后你解释说事情确实会被渲染......
@tungd 你能解释一下格林的问题吗what does it mean some branch is moved to another root? What do you call branch? What do you call root?
B
Brad Parks

即使在此处的许多其他答案中都有说明,该组件也应该:

实现 shouldComponentUpdate 以仅在状态或属性更改时呈现

切换到扩展 PureComponent,它已经在内部实现了一个 shouldComponentUpdate 方法以进行浅比较。

这是一个使用 shouldComponentUpdate 的示例,它仅适用于这个简单的用例和演示目的。使用此选项时,组件不再在每次单击时重新呈现自身,而是在第一次显示和单击一次后呈现。

var TimeInChild = React.createClass({ render: function() { var t = new Date().getTime(); return (

Time in child:{t}

); } }); var Main = React.createClass({ onTest: function() { this.setState({'test':'me'}); }, shouldComponentUpdate: function(nextProps, nextState) { if (this.state == null) return true; if (this.state.test == nextState.test) return false; return true; }, render: function() { var currentTime = new Date().getTime(); return (

Time in main:{currentTime}

点我更新时间

); } }); ReactDOM.render(
, document.body);


P
Penny Liu

是的。每次我们调用 setState 时它都会调用 render() 方法,除非 shouldComponentUpdate 返回 false


请再详细一点
D
Danny Harding

似乎在使用 React 钩子时,已接受的答案不再是这种情况(使用原始值,有关详细信息,请参阅此答案的评论)。您可以在此代码沙箱中看到,当状态设置为相同的值时,类组件会重新渲染,而在函数组件中,将状态设置为相同的值不会导致重新渲染。

https://codesandbox.io/s/still-wave-wouk2?file=/src/App.js


这是因为在您的示例中,您将原始值传递给 useState 挂钩,并将复杂对象传递给 setState 方法。尝试将对象或数组传递给 useState 挂钩以查看函数组件中的重新渲染,因为您正在更改对象引用。只是为了澄清你的例子,但你是对的,使用带有原始值的 useState 钩子可以防止重新渲染。
@MichalSvorc 确实,如果我们在 useState 中使用了一个对象,那么它也会重新渲染,但关键是不可能在类组件的 setState 函数中使用简单的原语。该示例仅显示了使用最简单的类组件状态形式和最简单的 useState 形式之间的区别,以及 hooks 版本如何(稍微)更高性能👍
当然,我只是添加了一个解释,说明为什么在您的示例中 useState 没有重新渲染。有人可能会将一个对象传递给 useState(这是合法的),认为该钩子会自动阻止重新渲染,因为您说过“使用 React 钩子时,接受的答案不再是这种情况”。有人可能不会立即发现使用原始引用和对象引用的区别。
有道理,谢谢。我在答案中添加了一个小的说明
Z
Zoltán Krizsán

“丢失更新”的另一个原因可能是下一个:

如果定义了静态 getDerivedStateFromProps,那么它会根据官方文档 https://reactjs.org/docs/react-component.html#updating 在每个更新过程中重新运行。

因此,如果该状态值一开始来自道具,则在每次更新时都会被覆盖。

如果是问题,那么 U 可以避免在更新期间设置状态,您应该像这样检查状态参数值

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

另一种解决方案是在状态中添加一个初始化属性,并在第一次设置它(如果状态被初始化为非空值。)


l
lifeisfoo

反应 18 及以后

从 React 18 all state updates are automatically batched 开始,将多个状态更新分组到一个重新渲染中以获得更好的性能。

因此,当您更新您的状态时,React 总是尝试在一个组更新中批量更新这些更新,导致 render 少于 setState 调用使用钩子时的行为是相同的

您可以在 Automatic batching for React 18 announcement 中阅读很长的说明。

反应 17 及以下

在 React 17 及更低版本中,仅对 React 事件处理程序内部的更新进行批处理。默认情况下,Promise、setTimeout、本机事件处理程序或任何其他事件内部的更新不会在 React 中批处理。


S
Singhi John

并非所有组件。

组件中的state看起来像是整个APP状态瀑布的来源。

因此,更改发生在 setState 调用的位置。然后从那里调用 renders 的树。如果您使用了纯组件,则 render 将被跳过。


b
boaz levinson

不管这里有什么解释清楚的答案,可能还有其他原因导致您在更改道具或状态后看不到预期的变化:

注意代码中的任何 event.preventDefault();,如果您希望通过 state \ props 更改重新渲染,因为它将取消此语句之后的任何可取消事件。


C
Ciocoiu Ionut Marius

只有在比较当前状态值和新状态值之后才能使用 setState() 并且它们是不同的。


很好的评论,虽然缺乏详细的答案
您使用 setState() 来更新组件的本地状态(状态内部的变量)。仅当 state 中的变量已更改并且组件再次呈现时才使用 setState()。如果您不想再次渲染组件,您可以使用 useRef(),或者您也可以使用某个变量再次渲染该组件并调度一些操作以强制再次渲染该组件。在某些情况下,建议仅使用 let 或 const 来管理变量。请同时考虑 useMemo,以避免在不需要时再次渲染组件。
您的问题下方有一个编辑按钮,可让您在发布后在答案中添加详细信息

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅