🪄 这么优雅?`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;

🎉 跑起来后会发生什么?

  • 点击 切换主题 按钮,Apptheme 状态变了
  • 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 管理啥全局状态?

相关推荐
Mr.Jessy3 小时前
JavaScript高级:构造函数与原型
开发语言·前端·javascript·学习·ecmascript
白兰地空瓶5 小时前
🚀你以为你在写 React?其实你在“搭一套前端操作系统”
前端·react.js
爱上妖精的尾巴5 小时前
6-4 WPS JS宏 不重复随机取值应用
开发语言·前端·javascript
似水流年QC6 小时前
深入探索 WebHID:Web 标准下的硬件交互实现
前端·交互·webhid
陪我去看海6 小时前
测试 mcp
前端
speedoooo7 小时前
在现有App里嵌入一个AI协作者
前端·ui·小程序·前端框架·web app
全栈胖叔叔-瓜州7 小时前
关于llamasharp 大模型多轮对话,模型对话无法终止,或者输出角色标识User:,或者System等角色标识问题。
前端·人工智能
三七吃山漆7 小时前
攻防世界——wife_wife
前端·javascript·web安全·网络安全·ctf
用户47949283569157 小时前
面试官问"try-catch影响性能吗",我用数据打脸
前端·javascript·面试
GISer_Jing7 小时前
前端营销技术实战:数据+AI实战指南
前端·javascript·人工智能