React加载屏适配主题切换问题求助
解决方案
一、修复初始加载屏的主题同步问题
当前你的加载屏脚本只读取了系统主题偏好,但没考虑用户手动切换后存在localStorage里的主题设置,先修改index.html里的脚本:
<script> function setLoadTheme() { // 优先读取用户手动保存的主题,没有再用系统默认偏好 const savedTheme = localStorage.getItem('themeName'); const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const theme = savedTheme || (darkMediaQuery.matches ? 'dark' : 'light'); document.getElementById('loader').className = `divLoader ${theme}`; } setLoadTheme(); </script>
二、修正ThemeContext的初始化逻辑
原来的ThemeProvider没有从localStorage读取用户设置,导致初始主题和加载屏不一致,同时补充系统主题变化的处理逻辑(仅当用户未手动设置时才跟随系统):
import { createContext, useEffect, useState } from 'react' import PropTypes from 'prop-types' const ThemeContext = createContext() function ThemeProvider({ children }) { // 初始化时优先取本地存储的主题,没有则用系统偏好 const [themeName, setThemeName] = useState(() => { const savedTheme = localStorage.getItem('themeName'); if (savedTheme) return savedTheme; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }) useEffect(() => { const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') // 系统主题变化时,仅在用户未手动设置的情况下更新 const handleSystemChange = (e) => { if (!localStorage.getItem('themeName')) { setThemeName(e.matches ? 'dark' : 'light'); } } darkMediaQuery.addEventListener('change', handleSystemChange) // 监听其他标签页的主题修改(多标签同步) const handleStorageChange = (e) => { if (e.key === 'themeName') { setThemeName(e.newValue); } } window.addEventListener('storage', handleStorageChange); return () => { darkMediaQuery.removeEventListener('change', handleSystemChange); window.removeEventListener('storage', handleStorageChange); } }, []) const toggleTheme = () => { const name = themeName === 'dark' ? 'light' : 'dark' localStorage.setItem('themeName', name) setThemeName(name) // 同步更新加载屏主题(如果加载屏还存在) const loader = document.getElementById('loader'); if (loader) loader.className = `divLoader ${name}`; } return ( <ThemeContext.Provider value={{ themeName, toggleTheme }}> {children} </ThemeContext.Provider> ) } ThemeProvider.propTypes = { children: PropTypes.node.isRequired, } export { ThemeProvider, ThemeContext }
三、React渲染完成后隐藏初始加载屏
在你的根组件(比如App.js)里,添加隐藏加载屏的逻辑:
import { useEffect } from 'react'; function App() { useEffect(() => { // React渲染完成后隐藏初始加载屏 const loader = document.getElementById('loader'); if (loader) loader.style.display = 'none'; }, []); return ( {/* 你的应用内容 */} ); } export default App;
四、路由切换时的主题同步加载屏
如果需要在路由切换时也显示加载屏,创建一个关联ThemeContext的加载组件:
// Loader.js import { useContext } from 'react'; import { ThemeContext } from './Theme'; export default function Loader() { const { themeName } = useContext(ThemeContext); return ( <div className={`divLoader ${themeName}`}> <svg className="svgLoader" viewBox="0 0 100 100" width="10em" height="10em"> <path stroke="none" d="M10 50A40 40 0 0 0 90 50A40 42 0 0 1 10 50" fill="#515390" transform="rotate(179.719 50 51)"> <animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 51;360 50 51" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform> </path> </svg> </div> ); }
然后在路由组件中用react-router-dom的useNavigation控制显示:
import { useNavigation } from 'react-router-dom'; import Loader from './Loader'; function App() { const navigation = useNavigation(); const isLoading = navigation.state === 'loading'; return ( <> {isLoading && <Loader />} {/* 你的路由内容 */} </> ); }
内容的提问来源于stack exchange,提问作者Jishnu
相关产品推荐
相关产品推荐

