Hook组合的艺术:react-custom-hooks的useDarkMode如何组合useMediaQuery与useLocalStorage
2026/8/23 12:15:40 网站建设 项目流程

Hook组合的艺术:react-custom-hooks的useDarkMode如何组合useMediaQuery与useLocalStorage

【免费下载链接】react-custom-hooksReact Custom Hooks @ S.Leschev: useArray useAsync useClickOutside useCookie useCopyToClipboard useDarkMode useDebounce useDebugInformation useDeepCompareEffect useEffectOnce useEventListener useFetch useGeolocation useHover useLongPress useMediaQuery useOnlineStatus useOnScreen usePrevious useRenderCount useScript etc.项目地址: https://gitcode.com/gh_mirrors/re/react-custom-hooks

🌙React 自定义 Hook是复用状态逻辑的最佳方式。在 react-custom-hooks 开源项目中,useDarkMode是教科书级的组合范例:它把useMediaQuery(监听系统深色模式偏好)和useLocalStorage(记住用户手动选择)两个基础 Hook 巧妙拼装在一起,仅用十几行代码就实现了"跟随系统 + 可手动切换 + 刷新不丢失"的完整深色模式功能。本文将带你理解这个 React 自定义 Hook 组合的设计思路。

🎯 先搞懂:什么是 React 自定义 Hook

React 16.8 引入 Hooks 后,开发者可以用useStateuseEffect在函数组件中管理状态。而自定义 Hook则是"用 Hook 写 Hook"——以use开头命名,把复杂逻辑封装成可复用的函数。

react-custom-hooks 项目提供了 20+ 个生产级自定义 Hook:useArrayuseAsyncuseDebounceuseFetchuseGeolocation等,覆盖状态管理、事件监听、存储、媒体查询等常见场景。其中useDarkMode最能体现Hook 组合的艺术:一个 Hook 调用另外两个 Hook,层层组合,各司其职。

🔍 三大 Hook 分工:一个"功能金字塔"

useDarkMode的底层由 3 个 Hook 支撑,形成清晰的三层结构:

层级Hook职责源码位置
顶层useDarkMode组合决策,输出最终开关状态src/hooks/useDarkMode/useDarkMode.js
中层useMediaQuery监听系统prefers-color-scheme变化src/hooks/useMediaQuery/useMediaQuery.js
中层useLocalStorage持久化用户手动选择src/hooks/useStorage/useStorage.js
底层useEventListeneruseMediaQuery提供事件绑定能力src/hooks/useEventListener/useEventListener.js

💡 注意一个细节:项目里并没有独立的useLocalStorage文件,useLocalStorageuseStorage模块对外导出的工厂函数(见src/hooks/useStorage/useStorage.js),同时它还导出了useSessionStorage,一份代码两种存储。

🧩 组合逻辑拆解:一行代码决定最终状态

打开src/hooks/useDarkMode/useDarkMode.js,核心逻辑只有 5 行:

const [darkMode, setDarkMode] = useLocalStorage("useDarkMode") const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)") const enabled = darkMode ?? prefersDarkMode

这段代码的设计精髓在于??(空值合并运算符)

  1. 用户手动切换过darkMode有值(truefalse),以用户选择为准;
  2. 用户从未操作darkModenull,自动降级到系统偏好prefersDarkMode

接下来通过useEffect把状态同步到<body>

useEffect(() => { document.body.classList.toggle("dark-mode", enabled) }, [enabled])

配套的样式只需一行 CSS(见src/hooks/useDarkMode/body.css):

body.dark-mode { background-color: #333; }

⚙️ 底层如何实时响应系统变化?

useMediaQuery的巧妙之处在于:它先通过window.matchMedia拿到 MediaQueryList 对象,再借助useEventListener监听其change事件,从而在系统主题切换的瞬间触发组件重渲染。

这里useEventListeneruseRef保存最新回调,避免了"闭包陷阱"——事件处理器不会因为组件重渲染而失效或重复绑定。这正是 Hook 组合能层层叠加的底气:每一层都只做一件事,且接口干净

🚀 新手上手:3 步接入 useDarkMode

第 1 步:克隆项目到本地(仓库地址:https://gitcode.com/gh_mirrors/re/react-custom-hooks)。

第 2 步:在任意组件中引入 Hook,参考官方示例组件src/hooks/useDarkMode/DarkModeComponent.js的用法:

import useDarkMode from "./useDarkMode" function App() { const [darkMode, setDarkMode] = useDarkMode() return <button onClick={() => setDarkMode(!darkMode)}>切换主题</button> }

第 3 步setDarkMode支持函数式更新,切换逻辑会写入localStorage,刷新页面后依然保留 👍

💡 为什么这个组合模式值得学习

  • 关注点分离:媒体查询、持久化、DOM 同步各在一个 Hook 里,单独可测试、可复用;
  • 零重复代码useMediaQueryuseLocalStorage本身就是通用 Hook,换个项目直接搬走;
  • 优雅降级??让"用户优先、系统兜底"的策略只花一行代码实现;
  • 状态即 API:返回[enabled, setDarkMode]数组,与useState签名一致,上手零成本。

📌 小结

useDarkMode展示了 React 自定义 Hook 的黄金法则:Hook 是可以嵌套组合的乐高积木。想在自己的项目中实现深色模式?不用引入任何状态管理库,复制src/hooks/useDarkMode/useDarkMode.jssrc/hooks/useMediaQuery/useMediaQuery.jssrc/hooks/useStorage/useStorage.jssrc/hooks/useEventListener/useEventListener.js四个文件即可开工。掌握这种组合思维,你就能用基础 Hook 拼出useOnlineStatususeWindowSize等任何"监听 + 存储 + 副作用"三件套功能。

【免费下载链接】react-custom-hooksReact Custom Hooks @ S.Leschev: useArray useAsync useClickOutside useCookie useCopyToClipboard useDarkMode useDebounce useDebugInformation useDeepCompareEffect useEffectOnce useEventListener useFetch useGeolocation useHover useLongPress useMediaQuery useOnlineStatus useOnScreen usePrevious useRenderCount useScript etc.项目地址: https://gitcode.com/gh_mirrors/re/react-custom-hooks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询