每次调用 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}
点我更新时间
setState()
也会导致元素呈现不同,所以我会说是的。当某些事情可能发生变化时,它绝对应该尝试重新渲染您的对象,否则您的演示 - 假设它是预期的行为 - 将无法工作!
shouldComponentUpdate
方法,我假设它的简单版本必须已经包含在 React 本身中。听起来像 react 中包含的默认版本只是返回 true
- 这会强制组件每次重新渲染。
每次调用 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 不会渲染所有内容。
每当一个组件变脏(其状态改变)时,该组件及其子组件都会重新渲染。这在某种程度上是为了尽可能少地重新渲染。唯一不调用 render 的时候是某个分支移动到另一个根目录时,理论上我们不需要重新渲染任何东西。在您的示例中, TimeInChild 是 Main 的子组件,因此当 Main 的状态发生变化时,它也会重新呈现。
React 不比较状态数据。当调用 setState 时,它会将组件标记为脏(这意味着它需要重新渲染)。需要注意的重要一点是,尽管调用了组件的 render 方法,但只有在输出与当前 DOM 树不同时才会更新真实 DOM(也就是虚拟 DOM 树和文档的 DOM 树之间的差异)。在您的示例中,即使状态数据没有更改,但上次更改的时间确实发生了变化,这使得 Virtual DOM 与文档的 DOM 不同,因此 HTML 会更新。
render()
方法应该是“纯”的——独立于外部状态。
some branch is moved to another root
是什么意思?你怎么称呼branch
?你怎么称呼root
?
what does it mean some branch is moved to another root? What do you call branch? What do you call root?
即使在此处的许多其他答案中都有说明,该组件也应该:
实现 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}
点我更新时间
是的。每次我们调用 setState
时它都会调用 render()
方法,除非 shouldComponentUpdate
返回 false
。
似乎在使用 React 钩子时,已接受的答案不再是这种情况(使用原始值,有关详细信息,请参阅此答案的评论)。您可以在此代码沙箱中看到,当状态设置为相同的值时,类组件会重新渲染,而在函数组件中,将状态设置为相同的值不会导致重新渲染。
https://codesandbox.io/s/still-wave-wouk2?file=/src/App.js
setState
函数中使用简单的原语。该示例仅显示了使用最简单的类组件状态形式和最简单的 useState 形式之间的区别,以及 hooks 版本如何(稍微)更高性能👍
“丢失更新”的另一个原因可能是下一个:
如果定义了静态 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};
}
另一种解决方案是在状态中添加一个初始化属性,并在第一次设置它(如果状态被初始化为非空值。)
反应 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 中批处理。
并非所有组件。
组件中的state
看起来像是整个APP状态瀑布的来源。
因此,更改发生在 setState 调用的位置。然后从那里调用 renders
的树。如果您使用了纯组件,则 render
将被跳过。
不管这里有什么解释清楚的答案,可能还有其他原因导致您在更改道具或状态后看不到预期的变化:
注意代码中的任何 event.preventDefault();
,如果您希望通过 state \ props 更改重新渲染,因为它将取消此语句之后的任何可取消事件。
只有在比较当前状态值和新状态值之后才能使用 setState() 并且它们是不同的。
setState
?如果是这样,如何做到最好 - 比较 json 字符串,构造和比较对象哈希,...?