React:当父组件重新渲染时,子组件是否总是重新渲染?

React: Do children always rerender when the parent component rerenders?

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

我有 3 个组件:<DynamicParent/><StaticParent/><Child/><Parent/> 组件负责呈现 <Child/> 但以不同的方式呈现。

<StaticParent/> 的渲染函数在运行前静态声明了 <Child/>,像这样:

 <StaticParent>
    <Child />
 </StaticParent>

虽然 <DynamicParent/> 在运行时动态处理接收和呈现 <Child/>,如下所示:

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

<DynamicParent/><StaticParent/> 都有 onClick 个侦听器来更改它们的状态并在单击时重新呈现。我注意到当单击 <StaticParent/> 时,它和 <Child/> 都会重新呈现。但是当我单击 <DynamicParent/> 时,只有父项而不是 <Child/> 被重新渲染。

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

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

我会post你的实际上下文代码:

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) },
);

在这两种情况下都是 Child:

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

这是什么意思?那么,在您的 StaticParent 组件中,每次调用 StaticParent 的 render 方法时,您都会调用 React.createElement(Child, null)。在 DynamicParent 的情况下, Child 被创建一次并作为道具传递。并且由于 React.createElement 是一个纯函数,所以它可能会在某处被记住以提高性能。

Child 在 DynamicParent 情况下再次渲染 运行 的原因是 Child 的道具发生了变化。例如,如果 parent 的状态被用作 Child 的道具,那么在这两种情况下都会触发 re-render。

我真的希望 Dan Abramov 不会出现在评论中来破坏这个答案,写起来很痛苦(但很有趣)

这主要是因为你有两个不同的"children"。

  • this.props.children
  • <Child/>

它们不是一回事,第一个是从 Application -> DynamicParent 传下来的 prop,而第二个是 StaticParent 中呈现的 Component,他们有单独的 rendering/life 周期。

您包含的示例

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>
    );
  }
}

作为对 SrThompsons 回答的评论:"What would make Child's render run again in the DynamicParent case is a change in Child's props. If the parent's state was used as a prop to the Child, for example, that would trigger a re-render in both cases." 所以道具或不道具传递给 child 组件但是它可能看起来会导致重新渲染如果 parent 重新渲染(所以使用 React.memo 用于 child 没有道具 :))

"Whether you’re implementing your component as a class component that extends React.Component, or as a functional component, the render function is called again whenever the parent container renders again." 请阅读此处了解更多信息,因为这篇文章很棒。 https://medium.com/free-code-camp/yeah-hooks-are-good-but-have-you-tried-faster-react-components-e698a8db468c"