基于其他组件状态更新组件: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
问题分析
- 状态不同步:TodoHeader和Switcher各自调用
useDarkSide,每个组件维护独立的theme状态。Switcher修改localStorage后,TodoHeader的theme状态不会自动更新(useState初始值仅挂载时读取一次),导致组件不重新渲染,Hamburger的color属性无法更新。 - 逻辑混淆:
useDarkSide返回的colorTheme是当前主题的反值,Switcher中调用setTheme(colorTheme)相当于把主题设置为当前的反值,但跨组件时这个状态无法同步。 - 无监听机制:没有监听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
相关产品推荐
相关产品推荐

