You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 14:20:42