ReactJS - 在调用"setState"的任何时候都会调用render吗?

Bra*_*rks 445 javascript reactjs

React是否每次都会重新渲染所有组件和子组件setState

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

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

this.setState({'test':'me'});
Run Code Online (Sandbox Code Playgroud)

我希望渲染只会在state数据发生变化时发生.

以下是该示例的代码,作为JS Fiddle和嵌入式代码段:

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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

[1]: http://jsfiddle.net/fp2tncmb/2/
Run Code Online (Sandbox Code Playgroud)

Pet*_*etr 518

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

默认情况下 - 是的.

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

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

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

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

问题的下一部分:

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

我们称之为"渲染"的步骤分为两步:

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

  2. 原生DOM渲染:只有在虚拟DOM中更改并且根据需要更改时,React才会更改浏览器中的真实DOM节点 - 这就是React的优秀功能,它可以优化真正的DOM突变并使React快速运行.

  • 很棒的解释!在这种情况下,真正使React仍然闪耀的一件事是它不会改变*real*DOM,除非虚拟DOM已被更改.因此,如果我的渲染函数连续两次返回相同的东西,真正的DOM根本不会改变...谢谢! (42认同)
  • 另外,请研究使用React.PureComponent(https://reactjs.org/docs/react-api.html#reactpurecomponent).它只会更新(重新渲染)组件的状态或道具实际更改.但要注意,比较浅. (3认同)
  • 如果您的状态包含一个相对较大的javascript对象(共约1k个属性),将其渲染为一棵大型组件树(共约100个...)...应让渲染函数构造虚拟dom,还是应在设置之前状态,手动将新状态与旧状态进行比较,并且仅在发现有区别时才调用setState?如果是这样,如何做到最好-比较json字符串,构造和比较对象哈希,...? (2认同)

tun*_*ngd 91

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

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

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

  • @tungd,这意味着什么?一些分支被移动到另一个根?你叫什么叫`branch`?你叫什么叫`root`? (3认同)
  • 我真的更喜欢标记为正确答案的答案。我理解您对本机,“真实”方面的“渲染”的解释...但是,如果您从反应本机方面看,您必须说它再次渲染。幸运的是,它足够聪明,可以确定真正更改的内容并仅更新这些内容。对于新用户来说,这个答案可能会令人困惑,首先您说不,然后您说明事情确实得到了呈现... (2认同)

lif*_*foo 11

React 18 及更高版本

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

因此,当您更新状态时,React 总是尝试在组更新中批处理这些更新,从而导致渲染次数少于调用次数setState。使用 hooks 时的行为是相同的

您可以在React 18公告的自动批处理中阅读非常长的解释。

React 17 及以下版本

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


Bra*_*rks 10

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

  • 实现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);
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


lak*_*ith 7

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

  • 请再详细点 (11认同)

Zol*_*sán 5

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

  • 如果定义了静态 getDerivedStateFromProps,则根据官方文档https://reactjs.org/docs/react-component.html#updating ,它会在每个更新过程中重新运行。
  • 因此,如果该状态值一开始就来自 props,那么它会在每次更新时被覆盖。

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

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}
Run Code Online (Sandbox Code Playgroud)

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


Dan*_*ing 5

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

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

  • 这是因为在您的示例中,您将原始值传递给 useState 挂钩,并将复杂对象传递给 setState 方法。尝试将对象或数组传递给 useState 挂钩以查看函数组件中的重新渲染,因为您正在更改对象引用。只是为了澄清您的示例,但您是对的,使用带有原始值的 useState 挂钩可以防止重新渲染。 (3认同)