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

React自定义Hook状态变更未触发组件重渲染问题排查

问题原因与解决方案:React自定义Hook状态变更后组件不重渲染

问题原因

核心问题是组件间状态不共享:

  • 每个调用useColorTheme的组件(比如DrawerContainer和ThemeToggle)都会创建独立的useLocalStorage状态实例,它们的storedValue是各自组件的局部状态,并非全局共享。
  • useLocalStorage的初始值仅在组件挂载时读取一次localStorage,后续其他组件修改localStorage后,当前组件的状态不会自动同步更新,因为useState不会主动监听localStorage的变化。

解决方案

方案一:用React Context实现全局主题状态(推荐)

将主题状态提升到全局Context中,所有组件从Context获取和修改状态,确保状态统一同步。

  1. 创建ThemeContext
// ThemeContext.tsx
import { createContext, useContext, ReactNode } from 'react';
import useColorTheme from './useColorTheme';

interface ThemeContextType {
  theme: string;
  setTheme: (value: string | ((prev: string) => string)) => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useColorTheme();
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme必须在ThemeProvider内部使用');
  }
  return context;
}
  1. 修改组件使用Context获取状态
  • DrawerContainer.tsx
export default function DrawerContainer() {
  const { theme } = useTheme();

  return (
    <div className="flex items-center justify-between gap-2">
      <FiMenu size={30} className="lg:hidden mr-4 cursor-pointer" />
      <Image
        className="ml-10 mr-10 sm:mr-2 sm:ml-0 cursor-pointer"
        src={
          theme === "light"
            ? "/images/fiverr-logo.png"
            : "/images/fiverr-logo-white.png"
        }
        alt="logo"
        width={100}
        height={100}
      />
      {!user && <AuthButtons showUnderSmall />}
      <ThemeToggle />
    </div>
  );
}
  • ThemeToggle.tsx(示例)
export default function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

  return (
    <button onClick={toggleTheme}>
      {theme === 'light' ? '切换到深色模式' : '切换到浅色模式'}
    </button>
  );
}
  1. 在顶层组件包裹ThemeProvider
// App.tsx
import { ThemeProvider } from './ThemeContext';

function App() {
  return (
    <ThemeProvider>
      {/* 你的应用根组件 */}
      <DrawerContainer />
    </ThemeProvider>
  );
}

方案二:在useLocalStorage中监听localStorage变化

通过监听浏览器的storage事件,同步不同组件的localStorage状态(适合跨标签页同步场景,同一标签页内组件同步推荐用Context)

修改useLocalStorage.ts:

import { useState, useEffect } from "react";

const PREFIX = "my-app-";

export default function useLocalStorage(key: string, initialValue: string) {
  const prefixedKey = PREFIX + key;
  const [storedValue, setStoredValue] = useState(() => {
    if (typeof window === "undefined") {
      return initialValue;
    }

    try {
      const item = window.localStorage.getItem(prefixedKey);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  function setValue(value: unknown) {
    try {
      const valueToStore = 
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      if (typeof window !== "undefined") {
        window.localStorage.setItem(prefixedKey, JSON.stringify(valueToStore));
      }
    } catch (error) {
      console.log(error);
    }
  }

  // 监听localStorage变化,同步组件状态
  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === prefixedKey) {
        try {
          const newValue = e.newValue ? JSON.parse(e.newValue) : initialValue;
          setStoredValue(newValue);
        } catch (error) {
          console.log(error);
          setStoredValue(initialValue);
        }
      }
    };

    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [prefixedKey, initialValue]);

  return [storedValue, setValue];
}

总结

  • Context方案:是React官方推荐的全局状态管理方式,状态同步即时可靠,适合单页应用内的组件状态共享。
  • storage事件方案:主要用于跨标签页的状态同步,同一标签页内的组件状态同步可能存在局限性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:30:46