ReactJS -渲染在任何时候都被调用吗?是叫什么?

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

如果有,为什么?我认为React的想法是只渲染需要的部分-当状态改变时。

在下面这个简单的例子中,当文本被点击时,这两个类都会再次呈现,尽管在随后的点击中状态不会改变,因为onClick处理程序总是将state设置为相同的值:

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

我本以为只有在state数据发生变化时才会发生渲染。

下面是示例代码作为JS的小提琴和嵌入式代码片段:

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


return (
<p>Time in child:{t}</p>
);
}
});


var Main = React.createClass({
onTest: function() {
this.setState({'test':'me'});
},


render: function() {
var currentTime = new Date().getTime();


return (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});


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

342542 次浏览

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

默认情况下是。

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

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

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

默认情况下,shouldComponentUpdate总是返回true来防止 细微的bug发生时状态是突变的,但如果小心的话 始终将状态视为不可变的,并且从props和state到只读 在render()中,您可以使用 实现,将旧的道具和状态与其进行比较 替代品。< / p >

你问题的下一部分:

如果有,为什么?我认为React只在需要的时候渲染一点点——当状态改变的时候。

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

  1. 虚拟DOM渲染:当渲染方法被调用时,它会返回组件的一个新的虚拟DOM 结构。正如我之前提到的,当你调用setState()时,这个render方法总是被调用,因为shouldComponentUpdate默认总是返回true。因此,默认情况下,React中没有优化。

  2. 原生DOM渲染:React只会在虚拟DOM中改变你浏览器中的真实DOM节点,并且尽可能少地改变它们——这是React的伟大特性,它优化了真实DOM的突变,使React更快。

不,React不会在状态改变时渲染所有内容。

  • 每当一个组件是脏的(它的状态改变了),该组件及其子组件将被重新呈现。在某种程度上,这是为了尽可能少地重新渲染。唯一不调用渲染的情况是当某个分支被移动到另一个根时,理论上我们不需要重新渲染任何东西。在你的例子中,TimeInChildMain的子组件,所以当Main的状态改变时,它也会被重新呈现。

  • React不比较状态数据。当调用setState时,它将该组件标记为dirty(这意味着它需要重新呈现)。需要注意的重要一点是,尽管调用了组件的render方法,但只有当输出与当前DOM树不同(也就是虚拟DOM树和文档的DOM树之间的差异)时,才会更新真正的DOM。在您的示例中,尽管state数据没有更改,但最后一次更改的时间更改了,这使得Virtual DOM不同于文档的DOM,因此更新了HTML。

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

尽管在这里的许多其他答案中都有说明,但组件应该:

  • 实现shouldComponentUpdate只在状态或属性改变时才呈现

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

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

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


return (
<p>Time in child:{t}</p>
);
}
});


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 (
<div onClick={this.onTest}>
<p>Time in main:{currentTime}</p>
<p>Click me to update time</p>
<TimeInChild/>
</div>
);
}
});


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

并非所有组件。

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

所以改变发生在setState调用的地方。然后从那里调用renders的树。如果你使用了纯组件,render将被跳过。

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

如果是这个问题,你可以避免在更新过程中设置状态,你应该像这样检查状态参数值

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

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

当使用React钩子(使用原语值,详细信息见答案注释)时,接受的答案似乎不再是这样。您可以在这个代码沙箱中看到,当状态设置为相同的值时,类组件会重新呈现,而在函数组件中,将状态设置为相同的值不会导致重新呈现。

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

React 18及以上

从React 18 所有状态更新都是自动批处理的开始,将多个状态更新分组到单个重新渲染中以获得更好的性能。

所以当你更新你的状态时,React总是尝试在一个组更新中批处理这些更新,导致rendersetState调用更少。行为使用钩子时也是一样吗

你可以在自动批处理React 18公告中读到很长的解释。

React 17及以下

在React 17及以下版本中,只有React事件处理程序内部的更新是批处理的。在React中,承诺、setTimeout、本机事件处理程序或任何其他事件中的更新默认情况下都不会批处理。

除了这里解释得很清楚的答案,可能还有其他原因导致你在改变道具或状态后没有看到预期的变化:

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

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