如何为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
相关产品推荐
相关产品推荐

