javascript - React Context Provider all children re rendering - Stack Overflow

can someone explain to me why the next code re renders all children ponents from the providerimport {

can someone explain to me why the next code re renders all children ponents from the provider

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

const App = () => {
    const [theme, setTheme] = useState(false);
    console.log("App running");
    return (
        <ThemeContext.Provider value={{ theme, setTheme }} children={<Child1 />} />
    );
};

const Child1 = () => {
    console.log("Child1 running");
    return (
        <div className="child1">
            <Child2 />
        </div>
    );
};

const Child2 = () => {
    console.log("Child2 running");
    return (
        <div className="child2">
            <Child3 />
        </div>
    );
};

const Child3 = () => {
    const { theme, setTheme } = useContext(ThemeContext);
    console.log("Child3 running");
    return (
        <div className="child3">
            <p>{theme ? "dark" : "light"}</p>
            <button onClick={() => setTheme(!theme)}>Change theme</button>
        </div>
    );
};

export default App;

console everytime button is clicked, all ponents re rendering

App running
Child1 running
Child2 running
Child3 running
App running
Child1 running
Child2 running
Child3 running

but if context provider is wrapped in a ponent as follows

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState(false);
    console.log("ThemeProvider running");
    return (
        <ThemeContext.Provider value={{ theme, setTheme }} children={children} />
    );
};

const App = () => {
    console.log("App running");
    return <ThemeProvider children={<Child1 />} />;
};

const Child1 = () => {
    console.log("Child1 running");
    return (
        <div className="child1">
            <Child2 />
        </div>
    );
};

const Child2 = () => {
    console.log("Child2 running");
    return (
        <div className="child2">
            <Child3 />
        </div>
    );
};

const Child3 = () => {
    const { theme, setTheme } = useContext(ThemeContext);
    console.log("Child3 running");
    return (
        <div className="child3">
            <p>{theme ? "dark" : "light"}</p>
            <button onClick={() => setTheme(!theme)}>Change theme</button>
        </div>
    );
};

export default App;

console when button is clicked

ThemeProvider running
Child3 running
ThemeProvider running
Child3 running
ThemeProvider running
Child3 running

only the ponent consuming the context (and the ponent context provider) are re rendering

how exactly react manages this situation

EDIT:

react version is 17.0.1 btw

can someone explain to me why the next code re renders all children ponents from the provider

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

const App = () => {
    const [theme, setTheme] = useState(false);
    console.log("App running");
    return (
        <ThemeContext.Provider value={{ theme, setTheme }} children={<Child1 />} />
    );
};

const Child1 = () => {
    console.log("Child1 running");
    return (
        <div className="child1">
            <Child2 />
        </div>
    );
};

const Child2 = () => {
    console.log("Child2 running");
    return (
        <div className="child2">
            <Child3 />
        </div>
    );
};

const Child3 = () => {
    const { theme, setTheme } = useContext(ThemeContext);
    console.log("Child3 running");
    return (
        <div className="child3">
            <p>{theme ? "dark" : "light"}</p>
            <button onClick={() => setTheme(!theme)}>Change theme</button>
        </div>
    );
};

export default App;

console everytime button is clicked, all ponents re rendering

App running
Child1 running
Child2 running
Child3 running
App running
Child1 running
Child2 running
Child3 running

but if context provider is wrapped in a ponent as follows

import { createContext, useContext, useState } from "react";

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState(false);
    console.log("ThemeProvider running");
    return (
        <ThemeContext.Provider value={{ theme, setTheme }} children={children} />
    );
};

const App = () => {
    console.log("App running");
    return <ThemeProvider children={<Child1 />} />;
};

const Child1 = () => {
    console.log("Child1 running");
    return (
        <div className="child1">
            <Child2 />
        </div>
    );
};

const Child2 = () => {
    console.log("Child2 running");
    return (
        <div className="child2">
            <Child3 />
        </div>
    );
};

const Child3 = () => {
    const { theme, setTheme } = useContext(ThemeContext);
    console.log("Child3 running");
    return (
        <div className="child3">
            <p>{theme ? "dark" : "light"}</p>
            <button onClick={() => setTheme(!theme)}>Change theme</button>
        </div>
    );
};

export default App;

console when button is clicked

ThemeProvider running
Child3 running
ThemeProvider running
Child3 running
ThemeProvider running
Child3 running

only the ponent consuming the context (and the ponent context provider) are re rendering

how exactly react manages this situation

EDIT:

react version is 17.0.1 btw

Share Improve this question edited Jan 9, 2021 at 3:08 William Estrada asked Jan 9, 2021 at 2:22 William EstradaWilliam Estrada 1411 silver badge9 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

This happens because <Context.Provider> re-renders when its children prop does not share reference equality with the previous children prop.

In the first example, every time App is re-rendered, a new Child1 React element is created.

It's basically like if you were doing something like this:

const App = () => {
  const [theme, setTheme] = useState(false);
  console.log("App running");
  return React.createElement(ThemeContext.Provider, {
    value: {
      theme: theme,
      setTheme: setTheme
    },
    children: React.createElement(Child1, null) <= Notice how the children prop is new with every re-render
  });
};

which eventually re-renders Child1, Child2 and Child3.


In the second example, the React element Child1 is created once inside App, and it's passed down to ThemeProvider, which means that inside ThemeProvider you are actually referencing the same React element, and not creating a new one with every re-render, so in this case only the associated consumer ponent (Child3) will re-render.

const App = ({ children }) => {
  const [theme, setTheme] = useState(false);
  console.log("App running");
  return React.createElement(ThemeContext.Provider, {
    value: {
      theme: theme,
      setTheme: setTheme
    },
    children: children
  });
};

Good read about why this happens

In the first example the useState hook is being called in App which causes it to rebuild the children prop () as a new element that's being passed to the Provider, rebuilding subtree. In your second example, App is not being rerendered (as useState is moved down) so the children () prop passed to the Provider does not change and the subtree is not rebuilt.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744235051a4564433.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信