VueBloghyhero6

react 父子子组件穿透 useContext的用法。

2025-11-05 / 2025-11-05 / 37次浏览

简单说明下吧其实写useContext因为它是React原生自带所以用的代码还是挺多的,所以实际项目都是用的插件 zustand,这个做状态管理还是挺方便的,但是原生的这个useContext面试难免不问,还是大概总结记录一下。

react 父子子组件穿透 useContext的用法。

// LevelContext.js    
step1: 创建

import { createContext } from 'react';
export const LevelContext = createContext(1);

// Heading.js
step2: 使用

import { useContext } from 'react';
import { levelContext } from './LevelContext.js';

从 heading 组件从props中读取level:

export default function Heading({ children }) {
  // 从 Context 中获取 level
  const level = useContext(LevelContext);
  // 根据 level 动态渲染不同的标题
  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      return <div>{children}</div>;
  }
}

// Section.js
从刚刚的 levelContext 中读取值:
import { LevelContext } from './LevelContext';
export default function Heading({ level, children }) {
  return (
    // 使用 LevelContext.Provider 将 level 传递给子组件
    <LevelContext.Provider value={level}>
      <section>
        {children}
      </section>
    </LevelContext.Provider>
  );
}

useContext 只能在React组件中(不是循环或者条件里)立即调用hook
在顶层组件中,使用 Section 和 Heading 组件来测试 useContext 的穿透效果。

// App.js
import Section from './Section';
import Heading from './Heading';
export default function App() {
  return (
    <div>
      {/* 顶层 Section,level 为 1 */}
      <Section level={1}>
        <Heading>标题 1</Heading>
        {/* 嵌套 Section,level 为 2 */}
        <Section level={2}>
          <Heading>标题 2</Heading>
          {/* 再次嵌套 Section,level 为 3 */}
          <Section level={3}>
            <Heading>标题 3</Heading>
          </Section>
        </Section>
      </Section>
    </div>
  );
}

运行结果分析
<div>
  <section>
    <h1>标题 1</h1>
    <section>
      <h2>标题 2</h2>
      <section>
        <h3>标题 3</h3>
      </section>
    </section>
  </section>
</div>


本质就是创建一个建议状态树,
1.上下文更新会导致所以该消费Context的组件重新渲染,会导致性能问题,尤其是Context.Provider的value更新时
如果value是一个复杂的对象,且该对象引用发生变化(即使内容没有变化),也会触发重新渲染。

2. useContext 没有内置的浅比较或深比机制,会有不必要的渲染

3. 建议采用外部状态树例如zustand。

这条在补充下,如果是level是异步的情况,首先模拟一个api请求js
// api.js
export async function fetchlevel() {
    // 模拟一个异步请求,2秒后返回一个level值
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(Math.floor(Math.random() * 6) + 1); // 返回1到6的随机数
        }, 2000)
    })
}
// Section.js
在Section 组件中,我们通过fetchLevel 异步获取 level 值, 并将其通过 LevelContext.Provider 传递给子组件。

// Section.js
import { useState, useEffect } from 'react';
import { levelContext } from './LevelContext';
import { fetchLevel } from './api';

export default function Section({ children }) {
  const [level, setLevel] = useState(null); // 用于存储异步获取的 level 值
  const [loading, setLoading] = useState(false); // 表示加载状态

  useEffect(() => {
    async function getLevel() {
       const resLevel = await fetchLevel();
       setLevel(resLevel);
       setLoading(false);
    }
    getLevel();
  },[])

  if (loading) {
    // 正在加载中
    return (<div>Loading...</div>);
  }

  return (
    <LevelContext.Provider value={level}>
        <section>
            {children}
        </section>
    </LevelContext.Provider>
  )
}

// Heading.js
import { useContext } from 'react';
import { LevelContext } from './LevelContext';

export default function Heading({ children }) {
  const level = useContext(LevelContext);

  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    case 2:
      return <h2>{children}</h2>;
    case 3:
      return <h3>{children}</h3>;
    case 4:
      return <h4>{children}</h4>;
    case 5:
      return <h5>{children}</h5>;
    case 6:
      return <h6>{children}</h6>;
    default:
      return <div>{children}</div>;
  }
}

// App.js
import Section from './Section';
import Heading from './Heading';

export default function App() {
  return (
    <div>
      <Section>
        <Heading>标题 1</Heading>
        <Section>
          <Heading>标题 2</Heading>
          <Section>
            <Heading>标题 3</Heading>
          </Section>
        </Section>
      </Section>
    </div>
  );
}

实话说我觉得用着不如 zustand 简洁,传值太多了写的代码也太多了。