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

相关推荐
一斤代码1 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子1 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年2 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子2 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina2 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路3 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_3 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
甜瓜看代码3 小时前
1.
react.js·node.js·angular.js
伍哥的传说3 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409193 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app