React + Chakra UI 颜色模式切换后刷新/导航闪烁问题求助
问题描述
React + Chakra UI项目中,初始设置为深色模式,切换到浅色模式后刷新或导航,页面会先闪烁深色再切换到正确模式;反之亦然。通过打印useColorMode()的加载值,发现加载时会先返回light再返回dark(或顺序相反)。
相关代码
index.tsx
import { ChakraProvider, ColorModeScript } from "@chakra-ui/react" import * as React from "react" import * as ReactDOM from "react-dom/client" import { App } from "./App" import reportWebVitals from "./reportWebVitals" import * as serviceWorker from "./serviceWorker" import theme from "../src/theme" const container = document.getElementById("root") if (!container) throw new Error('Failed to find the root element'); const root = ReactDOM.createRoot(container) root.render( <React.StrictMode> <ChakraProvider theme={theme}> <ColorModeScript initialColorMode={theme.config.initialColorMode} /> <App /> </ChakraProvider> </React.StrictMode>, )
App.tsx
import HeaderNavbar from "./Components/Header/Header" import Footer from "./Components/Footer/Footer" import { Home } from "../src/Pages/Home/Home" import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import { About } from "../src/Pages/About/About"; export const App = () => { return ( <> <HeaderNavbar /> <Router> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </Router> <Footer/> </> ); };
theme.ts
import { extendTheme, type ThemeConfig } from '@chakra-ui/react' const config: ThemeConfig = { initialColorMode: "dark", useSystemColorMode: false, } const theme = extendTheme({ config }) export default theme
解决思路
这个问题是Chakra UI官方明确的已知问题,核心原因是React hydration过程中,初始颜色模式与本地存储的用户选择模式不一致,导致渲染时先显示初始模式,再切换到用户设置的模式,从而产生闪烁。以下是具体解决方案:
调整ColorModeScript的位置
ColorModeScript是直接操作DOM的脚本,必须放在ChakraProvider外部,确保在Chakra初始化前注入。修改index.tsx的渲染结构:root.render( <> <ColorModeScript initialColorMode={theme.config.initialColorMode} /> <React.StrictMode> <ChakraProvider theme={theme}> <App /> </ChakraProvider> </React.StrictMode> </> )动态同步本地存储的颜色模式
用户切换模式后,Chakra会将选择存在localStorage的chakra-ui-color-mode键中。初始化时优先读取该值,确保初始渲染直接使用用户的选择:// index.tsx中添加 const getInitialColorMode = () => { if (typeof window !== 'undefined') { const storedMode = window.localStorage.getItem('chakra-ui-color-mode'); return storedMode || theme.config.initialColorMode; } return theme.config.initialColorMode; }; // 渲染时传入动态获取的初始模式 root.render( <> <ColorModeScript initialColorMode={getInitialColorMode()} /> <React.StrictMode> <ChakraProvider theme={theme}> <App /> </ChakraProvider> </React.StrictMode> </> )添加初始加载的样式隐藏
在public/index.html的<head>中添加CSS,在颜色模式未初始化完成前隐藏页面,避免闪烁:<style> body { visibility: hidden; } body.chakra-ui-light, body.chakra-ui-dark { visibility: visible; } </style>Chakra会在颜色模式初始化后给
<body>添加对应的类名,页面只有在模式确定后才会显示。排查React StrictMode的影响
开发环境下React StrictMode会触发两次渲染,可能导致模式值被重复设置。可以暂时关闭StrictMode测试是否是该原因,生产环境下StrictMode不会产生此问题,或者调整逻辑避免重复触发模式切换。
内容的提问来源于stack exchange,提问作者SolusArctic

