javascriptreactjsreact-componentreact-context

react context rerenders every child of Provider?


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

when clicked on ChangeNumber button console displays

rendering app

Rerendering wrapper Component

Rerendering Number Component

Rerendering wrapper Component

Rerendering Text Component

and background color changes for both number and text.

React context is supposed to rerender only consumers for the provider right? why is it rerendering All the children of Provider?

I expected that only number changes keeping background color of Number and Text as same and below output from console once clicked on Change Number button since only consumers are supposed to rerender not the Text and Number components.

rendering app

what am I missing?

I took the code from sandbox


Solution

  • WrappedNumber and WrappedText re-render when you update a state in the App component because in the Virtual DOM they come in the same hierarchy as the Provider and when the parent component updates the child components are updated too.

    To avoid them from re-rendering you could provide them as children of App like

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