【问题标题】:React: Do children always rerender when the parent component rerenders?React:当父组件重新渲染时,子组件是否总是重新渲染?
【发布时间】:2018-10-07 17:57:14
【问题描述】:

据我所知,如果父组件重新渲染,那么它的所有子组件都会重新渲染,除非它们实现 shouldComponentUpdate()。我made an example 这似乎不是真的。

我有 3 个组件:<DynamicParent/><StaticParent/><Child/><Parent/> 组件负责渲染 <Child/>,但以不同的方式进行。

<StaticParent/>的渲染函数在运行前静态声明<Child/>,如下所示:

 <StaticParent>
    <Child />
 </StaticParent>

虽然&lt;DynamicParent/&gt; 处理在运行时动态接收和渲染&lt;Child/&gt;,如下所示:

 <DynamicParent>
    { this.props.children }
 </DynamicParent>

&lt;DynamicParent/&gt;&lt;StaticParent/&gt; 都有 onClick 侦听器来更改其状态并在单击时重新呈现。我注意到当单击&lt;StaticParent/&gt; 时,它和&lt;Child/&gt; 都会重新渲染。但是当我点击&lt;DynamicParent/&gt; 时,只会重新渲染父级而不是&lt;Child/&gt;

&lt;Child/&gt; 是一个没有shouldComponentUpdate() 的功能组件,所以我不明白为什么它不重新渲染。有人可以解释为什么会这样吗?我在文档中找不到与此用例相关的任何内容。

【问题讨论】:

标签: javascript reactjs higher-order-components


【解决方案1】:

我会发布你的上下文代码:

class Application extends React.Component {
  render() {
    return (
      <div>
        {/* 
          Clicking this component only logs 
          the parents render function 
        */}
        <DynamicParent>
          <Child />
        </DynamicParent>

        {/* 
          Clicking this component logs both the 
          parents and child render functions 
        */}
        <StaticParent />
      </div>
    );
  }
}

class DynamicParent extends React.Component {
  state = { x: false };
  render() {
    console.log("DynamicParent");
    return (
      <div onClick={() => this.setState({ x: !this.state.x })}>
        {this.props.children}
      </div>
    );
  }
}

class StaticParent extends React.Component {
  state = { x: false };
  render() {
    console.log("StaticParent");
    return (
      <div onClick={() => this.setState({ x: !this.state.x })}>
        <Child />
      </div>
    );
  }
}

function Child(props) {
  console.log("child");
  return <div>Child Text</div>;
}

当您在应用程序渲染中编写此代码时:

<StaticParent />

渲染的是这样的:

 <div onClick={() => this.setState({ x: !this.state.x })}>
    <Child />
 </div>

实际上,发生的事情(大致)是这样的:

function StaticParent(props) {
  return React.createElement(
    "div",
    { onClick: () => this.setState({ x: !this.state.x }) },
    React.createElement(Child, null)
  );
}

React.createElement(StaticParent, null);

当你像这样渲染你的 DynamicParent 时:

<DynamicParent>
    <Child />
</DynamicParent>

这就是实际发生的事情(再次粗略地说)

function DynamicParent(props) {
    return React.createElement(
        "div",
        { 
            onClick: () => this.setState({ x: !this.state.x }), 
            children: props.children 
        }
    );
}

React.createElement(
      DynamicParent,
      { children: React.createElement(Child, null) },
);

在这两种情况下,这都是孩子:

function Child(props) {
    return React.createElement("div", props, "Child Text");
}

这是什么意思?好吧,在您的 StaticParent 组件中,每次调用 StaticParent 的渲染方法时,您都会调用 React.createElement(Child, null)。在 DynamicParent 案例中, Child 被创建一次并作为道具传递。而且由于React.createElement 是一个纯函数,所以它可能为了性能而被记忆在某个地方。

在 DynamicParent 的情况下让 Child 的渲染再次运行的原因是 Child 的 props 发生了变化。例如,如果父级的状态被用作子级的道具,那么在这两种情况下都会触发重新渲染。

我真的希望丹·阿布拉莫夫不要出现在 cmets 上来破坏这个答案,写起来很痛苦(但很有趣)

【讨论】:

    【解决方案2】:

    这主要是因为你有两个不同的“孩子”。

    • this.props.children
    • &lt;Child/&gt;

    它们不是一回事,第一个是从Application -&gt; DynamicParent 传下来的prop,而第二个是在StaticParent 中渲染的Component,它们有单独的渲染/生命周期。

    您包含的示例

    class Application extends React.Component {
      render() {
        return (
          <div>
            {/* 
              Clicking this component only logs 
              the parents render function 
            */}
            <DynamicParent>
              <Child />
            </DynamicParent>
    
            {/* 
              Clicking this component logs both the 
              parents and child render functions 
            */}
            <StaticParent />
          </div>
        );
      }
    }
    

    字面意思是一样的:

    class Application extends React.Component {
      render() {
        // If you want <Child/> to re-render here
        // you need to `setState` for this Application component.
        const childEl = <Child />;
        return (
          <div>
            {/* 
              Clicking this component only logs 
              the parents render function 
            */}
            <DynamicParent>
              {childEl}
            </DynamicParent>
    
            {/* 
              Clicking this component logs both the 
              parents and child render functions 
            */}
            <StaticParent />
          </div>
        );
      }
    }
    

    【讨论】:

      【解决方案3】:

      作为对 SrThompsons 回答的评论:“在 DynamicParent 情况下,使 Child 的渲染再次运行的原因是 Child 的 props 发生了变化。例如,如果将 parent 的 state 用作 Child 的 prop ,那将触发重新- 在这两种情况下都渲染。” 所以道具或不道具传递给子组件但是如果父级重新渲染,它可能看起来会导致重新渲染(所以对于没有道具的孩子使用React.memo :))

      “无论您是将组件实现为扩展 React.Component 的类组件,还是作为功能组件,只要父容器再次渲染,就会再次调用渲染函数。”请在此处阅读以获取更多信息,因为文章很棒。 https://medium.com/free-code-camp/yeah-hooks-are-good-but-have-you-tried-faster-react-components-e698a8db468c"

      【讨论】:

        【解决方案4】:

        它只会重新渲染发生变化的组件。如果子组件上没有任何更改,则不会重新渲染。

        【讨论】:

        • 如果你看我的例子,在 中,它的 没有任何变化,但它仍然呈现。