javascript - React Context Provider 所有子级重新渲染

标签 javascript reactjs react-context

有人可以向我解释为什么下一个代码会重新渲染来自提供者的所有子组件

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;
控制台每次点击按钮,所有组件重新渲染
App running
Child1 running
Child2 running
Child3 running
App running
Child1 running
Child2 running
Child3 running
但是如果上下文提供程序被包装在一个组件中,如下所示
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;
单击按钮时的控制台
ThemeProvider running
Child3 running
ThemeProvider running
Child3 running
ThemeProvider running
Child3 running
只有使用上下文的组件(和组件上下文提供者)正在重新渲染
react 究竟是如何管理这种情况的
编辑:
react 版本是 17.0.1 btw

最佳答案

发生这种情况是因为 <Context.Provider>当它的子节点支持 时重新渲染不共享引用相等 与以前的 child Prop 。
在第一个示例中,每次 App被重新渲染,一个 Child1创建 react 元素。
基本上就像你在做这样的事情:

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
  });
};
最终重新渲染 Child1 , Child2Child3 .

在第二个示例中,React 元素 Child1App 内创建一次, 它被传递给 ThemeProvider , 这意味着在 ThemeProvider您实际上是在引用 相同 react 元素,并且不会在每次重新渲染时创建一个新元素,因此在这种情况下,只有关联的消费者组件( Child3 )会重新渲染。
Good read about why this happens

关于javascript - React Context Provider 所有子级重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65638750/

相关文章:

reactjs - 如何将 React.createContext() 与 React Router 一起使用?

javascript - 未捕获的类型错误 : Cannot destructure property 'xxx' of 'useAuth(...)' as it is undefined

javascript - 在回调函数中将 THIS 对象作为 jQuery 选择器传递

javascript - 如何从 KnockoutJS 中的组件节点获取组件的 View 模型

javascript - 括号之间的简单 JavaScript 函数

Javascript else if 语句不适用于表单验证

css - react 如何为不同的组件打印横向/纵向

javascript - 刷新页面时 react 自定义模态显示模态内容几毫秒

javascript - React 在渲染之前更改 html,例如更改样式、添加 span 标签等

reactjs - 从网络获取数据时如何隐藏和显示进度条?