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

React中useEffect内theme变量赋值丢失警告的解决方法咨询

解决React暗黑模式中的theme变量警告问题

警告原因

你在组件顶层声明的theme属于渲染时创建的临时变量,每次组件重新渲染都会被重新声明并重置为undefined。在useEffect中对它的赋值仅在当前渲染周期有效,下次渲染时就会丢失,这就是React抛出警告的核心原因。

最佳解决方法

方法1:使用useRef持久化变量

useRef的.current属性会在组件整个生命周期内保持不变,适合存储不需要触发组件重渲染的可变值:

import React, { useEffect, useRef } from "react";
import { RiContrastLine } from 'react-icons/ri';

const DarkMode = () => {
    let body = "";
    if (typeof document !== "undefined") {
        body = document.body;
    }

    const clickedClass = "clicked";
    const lightTheme = "light-mode";
    const darkTheme = "dark-mode";
    // 用useRef创建持久化变量
    const themeRef = useRef();

    useEffect(() => {
        if (localStorage) {
            themeRef.current = localStorage.getItem("theme");
        }
        // 初始化主题
        const initialTheme = themeRef.current === lightTheme || themeRef.current === darkTheme 
            ? themeRef.current 
            : lightTheme;
        body.classList.add(initialTheme);
        themeRef.current = initialTheme;
    }, [])

    const switchTheme = (e) => {
        if (themeRef.current === darkTheme) {
            body.classList.replace(darkTheme, lightTheme);
            localStorage.setItem("theme", lightTheme);
            e.target.classList.remove(clickedClass);
            themeRef.current = lightTheme;
        } else {
            body.classList.replace(lightTheme, darkTheme);
            localStorage.setItem("theme", darkTheme);
            e.target.classList.add(clickedClass);
            themeRef.current = darkTheme;
        }
    };

    return (
        <button className='general-menu-button' type="button" onClick={(e) => switchTheme(e)}>
            <RiContrastLine />
        </button>
    );
};

export default DarkMode;

方法2:使用useState管理主题状态(更符合React规范)

如果后续需要根据主题状态更新组件UI(比如按钮样式、图标变化),用useState管理主题会更合适,因为状态变化会自动触发组件重渲染:

import React, { useEffect, useState } from "react";
import { RiContrastLine } from 'react-icons/ri';

const DarkMode = () => {
    let body = "";
    if (typeof document !== "undefined") {
        body = document.body;
    }

    const clickedClass = "clicked";
    const lightTheme = "light-mode";
    const darkTheme = "dark-mode";
    // 用useState管理主题状态
    const [theme, setTheme] = useState(null);

    useEffect(() => {
        // 从localStorage读取初始主题
        const savedTheme = localStorage.getItem("theme");
        const initialTheme = savedTheme === lightTheme || savedTheme === darkTheme 
            ? savedTheme 
            : lightTheme;
        setTheme(initialTheme);
        body.classList.add(initialTheme);
    }, [])

    const switchTheme = (e) => {
        if (theme === darkTheme) {
            setTheme(lightTheme);
            body.classList.replace(darkTheme, lightTheme);
            localStorage.setItem("theme", lightTheme);
            e.target.classList.remove(clickedClass);
        } else {
            setTheme(darkTheme);
            body.classList.replace(lightTheme, darkTheme);
            localStorage.setItem("theme", darkTheme);
            e.target.classList.add(clickedClass);
        }
    };

    return (
        <button className='general-menu-button' type="button" onClick={(e) => switchTheme(e)}>
            <RiContrastLine />
        </button>
    );
};

export default DarkMode;

额外优化建议

  • 把lightTheme、darkTheme这类固定常量提到组件外部,避免每次渲染重复创建
  • 可以封装一个自定义Hook抽离主题逻辑,方便在其他组件复用

内容的提问来源于stack exchange,提问作者W.C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:00:46