反应上下文重新渲染提供者的每个孩子?

Posted

技术标签:

【中文标题】反应上下文重新渲染提供者的每个孩子?【英文标题】:react context rerenders every child of Provider? 【发布时间】:2020-08-19 07:08:34 【问题描述】:
    import React,  useReducer, useEffect ,Component from "react";
    import ReactDOM from "react-dom";
    const AppContext = React.createContext();

import React from "react";

const withRandomColors = WrappedComponent => 
  return class RandomColors extends React.Component 
    constructor() 
      super();
      this.randomColors = [
        "red",
        "blue",
        "green",
        "cyan",
        "lavender",
        "skyblue",
        "orange",
        "pink",
        "yellow"
      ];
    

    getRandomColors() 
      const num = Math.floor(Math.random() * 10) % 9;
      return this.randomColors[num];
    

    render() 
       console.log("Rerendering wrapper Component");
      return <WrappedComponent randomColor=this.getRandomColors() />;
    
  ;
;

class Number extends React.Component 
  constructor(props) 
    super(props);
  

  componentDidMount() 

  render() 
    console.log("rendering Number Component");
    return (
      <AppContext.Consumer>
        ( number ) => 
          return (
            <div style= backgroundColor: `$this.props.randomColor` >
              number <br />
            </div>
          );
        
      </AppContext.Consumer>
    );
  


class Text extends React.Component 
  constructor(props) 
    super(props);
  

  render() 
     console.log("rendering Text Component");
    return (
      <AppContext.Consumer>
        ( text ) => (
          <div style= backgroundColor: `$this.props.randomColor` >
            text <br />
          </div>
        )
      </AppContext.Consumer>
    );
  


const WrappedText=withRandomColors(Text);
const WrappedNumber=withRandomColors(Number);

class App extends Component 
  constructor() 
    super();
    this.state = 
      number: Math.random() * 100,
      text: "testing context api"
    ;
  

  updateNumber = () => 
    const randomNumber = Math.random() * 100;
    this.setState( number: randomNumber );
  ;

  render() 
    console.log("rendering app")
    return (
      <AppContext.Provider value=this.state>
        <div>
          <h1>Welcome to React</h1>
          <WrappedNumber  />
          <WrappedText />
          <button onClick=this.updateNumber>Change Number </button>
        </div>
      </AppContext.Provider>
    );
  




ReactDOM.render(
  <App />,
  mountNode
);

当点击 ChangeNumber 按钮时控制台显示

rendering app

Rerendering wrapper Component

Rerendering Number Component

Rerendering wrapper Component

Rerendering Text Component

数字和文本的背景颜色会发生变化。

React 上下文应该只为提供者重新呈现消费者,对吗?为什么要重新渲染 Provider 的所有子节点?

我希望只有数字更改保持数字和文本的背景颜色相同,并在单击更改数字按钮后低于控制台输出,因为只有消费者应该重新呈现文本和数字组件。

rendering app

我错过了什么?

我从sandbox获取代码

【问题讨论】:

【参考方案1】:

WrappedNumberWrappedText 会在您更新 App 组件中的状态时重新渲染,因为在 Virtual DOM 中它们与 Provider 处于相同的层次结构中,并且当父组件更新时,子组件也会更新。

为了避免它们重新渲染,您可以将它们作为 App 的子级提供

class App extends Component 
  constructor() 
    super();
    this.state = 
      number: Math.random() * 100,
      text: "testing context api"
    ;
  

  updateNumber = () => 
    const randomNumber = Math.random() * 100;
    this.setState( number: randomNumber );
  ;

  render() 
    console.log("rendering app")
    return (
      <AppContext.Provider value=this.state>
        <div>
          <h1>Welcome to React</h1>
          this.props.children
          <button onClick=this.updateNumber>Change Number </button>
        </div>
      </AppContext.Provider>
    );
  


ReactDOM.render(
  <App >
      <WrappedNumber  />
      <WrappedText />
 </App>,
  mountNode
);

【讨论】:

好的。但是在创建 App 节点时我没有在运行时提供 WrappedText 和 WrappedNumber 儿童,而是在 App 的 render 方法中对它们进行了硬编码,但是两者都产生相同的结果,对吧?两者在反应如何处理这方面有什么区别吗? 正如我所说,不同之处在于如何使用虚拟 DOM 处理它们。当组件由于 state 或 props 改变而重新渲染时,任何直接在其 render 方法中重新渲染的组件都会被重新渲染。然而,上下文变化是专门向消费者传播的。当您将组件移出到 App 的父级并将它们呈现为子道具时,当 App 由于状态更改而重新渲染时,它们不会重新渲染

以上是关于反应上下文重新渲染提供者的每个孩子?的主要内容,如果未能解决你的问题,请参考以下文章

从消费者 componentDidMount 更新反应上下文会导致无限重新渲染

React Context API似乎重新渲染每个组件

为啥带有 useContext 触发器的自定义路由 HOC 会重新渲染?

反应我必须重新渲染父母才能重新渲染孩子吗?

反应父组件不重新渲染

有没有办法让上下文仅在其数据更改时重新渲染?