React Context Provider 所有子级重新渲染

Posted

技术标签:

【中文标题】React Context Provider 所有子级重新渲染【英文标题】:React Context Provider all children re rendering 【发布时间】:2021-04-14 17:40:33 【问题描述】:

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

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 顺便说一句

【问题讨论】:

【参考方案1】:

发生这种情况是因为 &lt;Context.Provider&gt; 在其 children prop不与之前的 children prop 共享引用相等时重新渲染。

在第一个示例中,每次重新渲染 App 时,都会创建一个 new 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
  );
;

最终会重新渲染 Child1Child2Child3


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

Good read about why this happens

【讨论】:

原始函数 createElement 比语法糖更清晰,至少对我来说是这样

以上是关于React Context Provider 所有子级重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

你应该用 React Context Provider 包装你的整个应用程序吗?

包含 Context.Provider 和 Context.Consumer 的 React Context 测试组件

使用React Context进行状态管理(五)Provider与Consumer匹配

React Context 和 Provider 数据没有按预期传递

06.React组件进阶(二)Context

React中的connect使用(Provider+Consumer例子 以及 contextType+this.context例子)