🪄 这么优雅?`useContext` + 自定义 Hooks:优雅管理全局状态,从主题切换说起


你是不是也遇到过?

  • 组件层级一深,props 传到怀疑人生...
  • 改个状态,祖孙八代组件都得改...
  • 一个主题色切换,改半天全局都没反应...

别怕!

这篇用最简单的「主题切换」例子,一口气教会你:

  • 什么是 useContext
  • Provider 怎么用
  • 自定义 useTheme 怎么封装
  • 组件怎么消费全局状态

先说结论:useContext 解决啥问题?

咱都知道,React 核心原则是:
单向数据流,父传子,props 一层层往下传。

可是------

当组件嵌套很深时:

xml 复制代码
<App>
  <Parent>
    <Child>
      <GrandChild>
        <GreatGrandChild />
      </GrandChild>
    </Child>
  </Parent>
</App>

如果最外层有个状态,最里层也需要用到,怎么办?

要么 props 一层层传(麻烦!)

要么用全局状态管理(Context!)

所以:

Context 本质就是:在组件树中共享状态,不用 props 一层层传递。


Context 三步走流程

别背概念,直接记死

✅ 第一步:创建上下文对象

js 复制代码
import { createContext } from 'react';

export const ThemeContext = createContext('light'); // 默认值

✅ 第二步:用 Provider 包住要用到它的组件

jsx 复制代码
import { ThemeContext } from './ThemeContext';

<ThemeContext.Provider value={theme}>
  <App />
</ThemeContext.Provider>

✅ 第三步:在需要的地方用 useContext 拿到状态

jsx 复制代码
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

const theme = useContext(ThemeContext);

完事儿。


看个完整主题切换示例

直接上代码,一把梭


ThemeContext.js:创建上下文

jsx 复制代码
import { createContext } from 'react';

// 默认值是 'light'
export const ThemeContext = createContext('light');

App.jsx:声明全局 Provider

jsx 复制代码
import { useState } from 'react';
import { ThemeContext } from './ThemeContext';
import Page from './components/Page';

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <Page />
      <button onClick={() => setTheme('dark')}>
        切换主题
      </button>
    </ThemeContext.Provider>
  );
}

export default App;

hooks/useTheme.js:封装自定义 Hook

jsx 复制代码
import { useContext } from 'react';
import { ThemeContext } from '../ThemeContext';

export function useTheme() {
  return useContext(ThemeContext);
}

components/Page.jsx:使用自定义 Hook

jsx 复制代码
import { useTheme } from '../hooks/useTheme';
import Child from './Child';

const Page = () => {
const theme = useTheme();

  return (
    <>
      <div>当前主题:{theme}</div>
      <Child />
    </>
  );
};

export default Page;

components/Child.jsx:子组件一样能用

jsx 复制代码
import { useTheme } from '../hooks/useTheme';

const Child = () => {
  const theme = useTheme();

  return (
    <div className={theme}>
      我是 Child,当前主题:{theme}
    </div>
  );
};

export default Child;

🎉 跑起来后会发生什么?

  • 点击 切换主题 按钮,App 的 theme 状态变了
  • ThemeContext.Provider 传给下面所有子组件的新值
  • 所有用 useTheme 的组件都能拿到最新值,完全不需要一层层 props!

✨ 为什么推荐配合自定义 Hooks?

有人会问:

直接 useContext(ThemeContext) 不香吗?

干嘛还要 useTheme 再包一层?

原因:

  • 复用:以后换全局主题管理方式,只改 useTheme,组件不动
  • 习惯:大团队里常见,把所有上下文都封装成 useXXX,一眼就知道干啥
  • 组合:可以在 useTheme 里加其他副作用逻辑,比如主题持久化

useContext 还有哪些坑?

简单提醒

✅ Provider 必须在外层包住,用错位置会报 undefined

✅ Context 适合放全局状态,不要滥用(业务局部用 props 就行)

✅ 需要多状态共享?就多个 Context + Provider 嵌套


🪄 再来个套路总结

  • createContext 创建上下文对象
  • Provider 全局声明可用范围 + 提供状态
  • useContext 消费上下文里的状态
  • 自定义 Hook 封装 useContext,解耦可复用

数据共享,肯定不止一种方法!

说到这,很多人会问:

"有了 Context,还要不要用 props?要不要用 Zustand / Redux?"

答案:

  • 单向数据流 props 依旧最轻便,能 props 就 props
  • Context 适合跨层级全局状态(用户信息、主题、语言等)
  • Zustand、Redux 适合更复杂的状态(异步、状态中转、状态快照)

咱写 React,不是把啥都塞到 Context 就行,选对场景最重要。


✅ 写在最后

今天一口气学了:

  • useContext 怎么解决组件传参烦恼
  • Provider 怎么声明
  • 自定义 Hook 怎么封装
  • ThemeContext 主题切换完整流程

结尾

看完有收获?点个赞 + 收藏吧!

评论区告诉我:

你平时还会用 Context 管理啥全局状态?

相关推荐
IT_陈寒7 小时前
Java中equals方法比了个寂寞?原来这才是正确的重写姿势
前端·人工智能·后端
默_笙7 小时前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript
CopyCode7 小时前
用 AI 迁项目有多爽?我把 Webpack 迁 Vite 的全过程记下来了
前端·架构
去伪存真7 小时前
Electron 自动化发布指南:GitHub Actions 跨平台打包全纪录
前端·electron
计算机魔术师7 小时前
OpenAI智能体失控闯进美国政府网站,53张用户图片外泄背后
前端
颜进强7 小时前
14 · NestJS ExecutionContext 执行上下文:守卫、拦截器、过滤器拿到的"同一个 context",为什么能力不一样?
前端·后端·ai编程
程序员Flycan7 小时前
🚀 跨域终结者:前端代理服务器(Proxy)原理解析与配置总结
前端
怕浪猫7 小时前
顶级模型一句话,AI 写出了能玩的 QQ飞车
前端·面试·github
huakoh7 小时前
MCP 报错分不清?先看响应里是 result 还是 error
前端
呃呃呃呃ex7 小时前
10. 现代前端工程化:ES6 React 项目实战与原理解析
前端·javascript