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

43
据我所知,如果父组件重新渲染,则其所有子组件都将重新渲染,除非它们实现了shouldComponentUpdate()。我制作了一个示例,其中似乎并不是这样。
我有三个组件:<DynamicParent/><StaticParent/><Child/><Parent/>组件负责呈现<Child/> ,但以不同的方式呈现。 <StaticParent/>的渲染函数在运行时之前静态声明了<Child/>,如下所示:
 <StaticParent>
    <Child />
 </StaticParent>

虽然 <DynamicParent/> 在运行时动态接收和渲染 <Child/>,但是它并不处理 <Child/> 的状态。

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

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

<Child/> 是一个没有 shouldComponentUpdate() 的函数组件,所以我不明白为什么它不会重新呈现。可以有人解释一下这是为什么吗?我在文档中找不到与此用例相关的任何信息。


展示更多的代码? - Leland B
Its in the link - Gabriel West
4个回答

42

我会为了上下文发布你的实际代码:

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 的属性,那么这将在两种情况下触发重新渲染。

我真的希望 Dan Abramov 不会在评论中出现,否则他可能会批评我的回答(虽然写起来很痛苦但也很有趣)。


8

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

  • this.props.children
  • <Child/>

它们不是同一件事,第一个是从Application -> 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>
    );
  }
}

2
作为对SrThompsons答案的评论:“在DynamicParent情况下,使Child的渲染再次运行的是Child的props的更改。例如,如果将父级状态用作Child的prop,那么这将触发两种情况下的重新渲染。” 因此,无论是否将props传递给子组件,只要父组件重新渲染,都会导致重新渲染(因此对于没有props的子组件使用React.memo :))。
“无论您将组件实现为扩展React.Component的类组件还是函数组件,每当父容器再次呈现时,都会再次调用render函数。”请阅读此处以获取更多信息,因为这是一篇非常好的文章。https://medium.com/free-code-camp/yeah-hooks-are-good-but-have-you-tried-faster-react-components-e698a8db468c

-3

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


4
如果您看一下我的示例,在<StaticParent/>中,它的<Child/>没有任何更改,但仍然被呈现出来。 - Gabriel West

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接