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

基于其他组件状态更新组件:Hamburger颜色切换需刷新问题

明暗模式切换时Hamburger组件颜色不实时更新问题

问题描述

使用hamburger-react的Hamburger组件时,该组件不支持className,只能通过color属性设置颜色。用三元运算符根据主题切换颜色时,只有页面刷新后颜色才会变化,但FontAwesomeIcon通过Tailwind的dark:text-gray-50类能正常切换明暗颜色。怀疑TodoHeader组件在主题变化时没有重新渲染,但不知道解决方法。

相关代码

TodoHeader.jsx

import Search from "./Search";
import Switcher from "../Switcher";
import { faBars } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Fade as Hamburger } from "hamburger-react";
import { useProSidebar } from "react-pro-sidebar";
import useDarkSide from "../../hooks/useDarkSide";

const TodoHeader = () => {
    const { collapseSidebar } = useProSidebar();
    const [colorTheme, setTheme] = useDarkSide();

    const toggle = () => {
        collapseSidebar()
    }

    return (
        <div className={"flex items-center justify-between border-b-2 dark:border-gray-600"}>
            <div className={"flex items-center ml-8"}>
                <Hamburger direction={"right"} onToggle={toggle} color={ colorTheme === 'light' ? "#f3f4f6" : "#1f2937"}/>
                <FontAwesomeIcon icon={faBars} onClick={toggle} className={"text-center dark:text-gray-50"}/>
                <h1 className={"p-4 text-2xl text-gray-800 dark:text-gray-100"}>Todo List</h1>
            </div>
            <Search/>
            <Switcher/>
        </div>
    );
};
export default TodoHeader;

useDarkSide.js

import {useEffect, useState} from "react";

const useDarkSide = () => {
    const [theme, setTheme] = useState(localStorage.theme);
    const colorTheme = theme === "dark" ? "light" : "dark";

    useEffect(() => {
        const root = window.document.documentElement;
        root.classList.remove(colorTheme);
        root.classList.add(theme);
        localStorage.setItem('theme', theme);
    }, [theme, colorTheme]);

    return [colorTheme, setTheme]
}
export default useDarkSide

Switcher.jsx

import {useState} from "react";
import { DarkModeSwitch} from "react-toggle-dark-mode";
import useDarkSide from "../hooks/useDarkSide";

const Switcher = () => {
    const [colorTheme, setTheme] = useDarkSide();
    const [darkSide, setDarkSide] = useState(
        colorTheme === "light" ? true : false
    );

    const toggleDarkMode = (checked) => {
        setTheme(colorTheme);
        setDarkSide(checked);
    };

    return (
        <>
            <DarkModeSwitch
                style={{ marginRight: '2rem' }}
                checked={darkSide}
                onChange={toggleDarkMode}
                size={30}
            />
        </>
    );
}
export default Switcher

问题分析

  1. 状态不同步:TodoHeader和Switcher各自调用useDarkSide,每个组件维护独立的theme状态。Switcher修改localStorage后,TodoHeader的theme状态不会自动更新(useState初始值仅挂载时读取一次),导致组件不重新渲染,Hamburger的color属性无法更新。
  2. 逻辑混淆:useDarkSide返回的colorTheme是当前主题的反值,Switcher中调用setTheme(colorTheme)相当于把主题设置为当前的反值,但跨组件时这个状态无法同步。
  3. 无监听机制:没有监听localStorage的变化,无法在组件间同步主题状态。

解决方案

1. 修复useDarkSide钩子,添加跨组件同步机制

修改钩子逻辑,明确主题状态,添加localStorage监听确保多组件状态同步:

import { useEffect, useState } from "react";

const useDarkSide = () => {
  // 初始化主题,默认light
  const [theme, setTheme] = useState(localStorage.theme || "light");

  // 切换主题的函数,直接取反当前主题
  const toggleTheme = () => {
    const newTheme = theme === "dark" ? "light" : "dark";
    setTheme(newTheme);
  };

  // 主题变化时更新根元素类名和localStorage
  useEffect(() => {
    const root = window.document.documentElement;
    root.classList.remove("dark", "light");
    root.classList.add(theme);
    localStorage.setItem("theme", theme);
  }, [theme]);

  // 监听localStorage变化,同步主题状态
  useEffect(() => {
    const handleStorageChange = (e) => {
      if (e.key === "theme") {
        setTheme(e.newValue);
      }
    };
    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, []);

  // 返回当前主题和切换函数,逻辑更清晰
  return [theme, toggleTheme];
};

export default useDarkSide;

2. 修改Switcher组件,使用正确的切换逻辑

基于修复后的钩子,简化Switcher的状态管理:

import { useState, useEffect } from "react";
import { DarkModeSwitch } from "react-toggle-dark-mode";
import useDarkSide from "../hooks/useDarkSide";

const Switcher = () => {
  const [theme, toggleTheme] = useDarkSide();
  const [darkSide, setDarkSide] = useState(theme === "dark");

  // 同步主题和开关状态
  useEffect(() => {
    setDarkSide(theme === "dark");
  }, [theme]);

  const toggleDarkMode = () => {
    toggleTheme();
  };

  return (
    <>
      <DarkModeSwitch
        style={{ marginRight: '2rem' }}
        checked={darkSide}
        onChange={toggleDarkMode}
        size={30}
      />
    </>
  );
};

export default Switcher;

3. 调整TodoHeader中的Hamburger颜色逻辑

根据修复后的钩子返回的当前主题,直接设置颜色:

import Search from "./Search";
import Switcher from "../Switcher";
import { faBars } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Fade as Hamburger } from "hamburger-react";
import { useProSidebar } from "react-pro-sidebar";
import useDarkSide from "../../hooks/useDarkSide";

const TodoHeader = () => {
  const { collapseSidebar } = useProSidebar();
  const [theme] = useDarkSide();

  const toggle = () => {
    collapseSidebar();
  };

  return (
    <div className={"flex items-center justify-between border-b-2 dark:border-gray-600"}>
      <div className={"flex items-center ml-8"}>
        <Hamburger 
          direction={"right"} 
          onToggle={toggle} 
          color={theme === 'light' ? "#1f2937" : "#f3f4f6"} // 根据当前主题设置对应颜色
        />
        <FontAwesomeIcon icon={faBars} onClick={toggle} className={"text-center dark:text-gray-50"} />
        <h1 className={"p-4 text-2xl text-gray-800 dark:text-gray-100"}>Todo List</h1>
      </div>
      <Search />
      <Switcher />
    </div>
  );
};

export default TodoHeader;

内容的提问来源于stack exchange,提问作者Kseylo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:50:19