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

如何为React Todo应用的不同页面单独设置明暗主题?

不同页面独立切换主题的实现方案

核心思路是把主题状态和页面路由绑定,不再用全局单一的主题值,而是用一个对象存储每个路由对应的主题,这样切换某页面主题时只会影响当前路由的状态。

1. 修改主题Context的状态结构

把原来全局单一的theme改成以路由路径为键的对象,每个页面的主题独立存储,默认未设置的页面都用浅色主题。

import { createContext, useContext, useState, useEffect } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  // 初始化状态:空对象,未设置的页面默认用light
  const [themeMap, setThemeMap] = useState({});

  // 获取当前页面主题的方法
  const getCurrentTheme = (pathname) => themeMap[pathname] || 'light';
  // 切换当前页面主题的方法
  const toggleCurrentTheme = (pathname) => {
    setThemeMap(prev => ({
      ...prev,
      [pathname]: prev[pathname] === 'dark' ? 'light' : 'dark'
    }));
  };

  return (
    <ThemeContext.Provider value={{ getCurrentTheme, toggleCurrentTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}

2. 在页面组件中绑定路由与主题

不管是函数组件还是类组件,都需要拿到当前页面的路由路径,然后从Context里获取对应主题,切换时只更新当前路径的主题状态。

函数组件(FunctionalToDo)示例

import { useLocation } from 'react-router-dom';
import { useTheme } from './ThemeContext';

export default function FunctionalToDo() {
  const location = useLocation();
  const { getCurrentTheme, toggleCurrentTheme } = useTheme();
  const currentTheme = getCurrentTheme(location.pathname);

  return (
    <div className={`todo-container ${currentTheme}`}>
      <h1>函数式待办列表</h1>
      <button onClick={() => toggleCurrentTheme(location.pathname)}>
        切换主题:{currentTheme === 'light' ? '深色' : '浅色'}
      </button>
      {/* 待办列表内容 */}
    </div>
  );
}

类组件(ClassToDo)示例

注:如果使用React Router v6,withRouter已被移除,可以自己封装一个高阶组件传递路由信息,或者直接将类组件转为函数组件使用useLocation。

import { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { ThemeContext } from './ThemeContext';

class ClassToDo extends Component {
  static contextType = ThemeContext;

  render() {
    const { getCurrentTheme, toggleCurrentTheme } = this.context;
    const { pathname } = this.props.location;
    const currentTheme = getCurrentTheme(pathname);

    return (
      <div className={`todo-container ${currentTheme}`}>
        <h1>类组件待办列表</h1>
        <button onClick={() => toggleCurrentTheme(pathname)}>
          切换主题:{currentTheme === 'light' ? '深色' : '浅色'}
        </button>
        {/* 待办列表内容 */}
      </div>
    );
  }
}

export default withRouter(ClassToDo);

3. 编写主题对应的样式

确保CSS里有浅色/深色主题的对应样式:

.todo-container.light {
  background-color: #ffffff;
  color: #2d3748;
}

.todo-container.dark {
  background-color: #2d3748;
  color: #ffffff;
}

4. 可选:持久化主题状态

如果需要页面刷新后保留主题设置,可以在ThemeProvider里用localStorage做持久化:

export function ThemeProvider({ children }) {
  // 从localStorage读取保存的主题状态
  const [themeMap, setThemeMap] = useState(() => {
    const saved = localStorage.getItem('themeMap');
    return saved ? JSON.parse(saved) : {};
  });

  // 主题变更时同步保存到localStorage
  useEffect(() => {
    localStorage.setItem('themeMap', JSON.stringify(themeMap));
  }, [themeMap]);

  // ... 其余代码不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:46:10