🪄 这么优雅?`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 管理啥全局状态?

相关推荐
鱼 空2 分钟前
解决el-table右下角被挡住部分
javascript·vue.js·elementui
柚子81612 分钟前
scroll-marker轮播组件不再难
前端·css
你的人类朋友1 小时前
🫏光速入门cURL
前端·后端·程序员
01传说1 小时前
vue3 配置安装 pnpm 报错 已解决
java·前端·vue.js·前端框架·npm·node.js
Misha韩1 小时前
React Native 一些API详解
react native·react.js
小李飞飞砖1 小时前
React Native 组件间通信方式详解
javascript·react native·react.js
小李飞飞砖1 小时前
React Native 状态管理方案全面对比
javascript·react native·react.js
烛阴2 小时前
Python装饰器解除:如何让被装饰的函数重获自由?
前端·python
千鼎数字孪生-可视化3 小时前
Web技术栈重塑HMI开发:HTML5+WebGL的轻量化实践路径
前端·html5·webgl
凌辰揽月3 小时前
7月10号总结 (1)
前端·css·css3