React Context主题切换异常:仅首次切换生效后续无法切换
明暗主题切换仅首次生效的问题修复
问题背景
正在实现React明暗主题切换功能,要求通过Context传递theme和toggleTheme。切换组件的onChange已绑定toggleTheme,首次切换正常,但后续无法来回切换。
错误代码分析
原ThemeContext代码存在几个关键问题:
- 状态更新时调用了不存在的
setT,正确应为setTheme - 初始状态中
toggleTheme被冗余包装成匿名函数,导致后续状态更新后函数引用异常 - 状态结构设计冗余,没必要把
toggleTheme存入状态对象
原错误代码:
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(undefined); export const ThemeProvider = ({ children }) => { const toggleTheme = (e) => { if (theme["theme"] === "dark") { setT({"theme":"light", toggleTheme}); // 错误:setT不存在,应为setTheme } else { setT({"theme":"dark", toggleTheme}); // 同样错误 } } const [theme, setTheme] = useState({ theme: "dark", toggleTheme: () => toggleTheme // 错误:无需包装成匿名函数,会引发引用问题 }); return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>; } export const useTheme = () => useContext(ThemeContext);
修复后的代码
优化后的ThemeContext
拆分状态管理,避免嵌套冗余,修正状态更新逻辑:
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(undefined); export const ThemeProvider = ({ children }) => { // 单独管理theme字符串状态,无需将toggleTheme存入状态 const [theme, setTheme] = useState("dark"); const toggleTheme = () => { // 使用函数式更新确保获取最新状态,避免闭包问题 setTheme(prevTheme => prevTheme === "dark" ? "light" : "dark"); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } export const useTheme = () => useContext(ThemeContext);
切换组件(Switch)
逻辑无需大幅调整,保持原有绑定即可:
import "./Styles.css"; import { useTheme } from "../ThemeContext"; const Switch = () => { const { theme, toggleTheme } = useTheme(); return ( <label className="switch"> <input type="checkbox" defaultChecked={theme === "light"} onChange={toggleTheme} /> <span className="slider round" /> </label> ); }; export default Switch;
段落组件(Paragraph)
原代码逻辑正确,可直接使用:
const Paragraph = ({ children }) => { const { theme } = useTheme(); return ( <p style={{ color: theme === "light" ? "black" : "white", }} > {children} </p> ); };
关键修复点说明
- 状态拆分:将
theme字符串单独作为状态,无需把toggleTheme存入状态对象,避免函数引用异常 - 函数式状态更新:使用
setTheme(prevTheme => ...)确保每次切换都能获取最新状态,规避闭包导致的旧状态问题 - 修正更新函数名:把错误的
setT改为正确的setTheme - 简化初始状态:初始状态仅设置
theme的初始值,toggleTheme作为组件内函数直接传入Context
内容的提问来源于stack exchange,提问作者Daniel Weisinger
相关产品推荐
相关产品推荐

