使用SolidJS children辅助方法后Theme Provider失效,setTheme非函数
问题:ThemeProvider修改后报错“setTheme不是函数”
原本的Solid.js ThemeProvider代码可以正常工作,但添加const c = children(() => props.children);并将返回的子元素改为{c()}后,出现**“setTheme不是函数”**的错误。
原正常代码
import { Accessor, JSXElement, Setter, children, createContext, createEffect, createSignal, onMount, useContext } from "solid-js"; import constants from "@/constants"; type TThemeProviderProps = { children: JSXElement; defaultTheme: string; }; type TThemeContext = [Accessor<string>, Setter<string>]; const ThemeKey = "stk-auth-theme"; const ThemeProvider = (props: TThemeProviderProps) => { const [theme, setTheme] = createSignal<string>(props.defaultTheme); onMount(() => { // 读取缓存 const cachedTheme = localStorage.getItem(ThemeKey); if (cachedTheme) { setTheme(cachedTheme); } else { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; setTheme(prefersDark ? "dark" : "light") } }); createEffect(() => { document.documentElement.classList.toggle(constants.DARK_MODE, theme() === constants.DARK_MODE); // 写入缓存 localStorage.setItem(ThemeKey, theme()); }); const values: TThemeContext = [theme, setTheme]; return <ThemeContext.Provider value={values}>{props.children}</ThemeContext.Provider>; }; export const ThemeContext = createContext<TThemeContext>([() => "", undefined]); export default ThemeProvider; export const useTheme = () => { return useContext(ThemeContext); };
修改后报错的代码
import { Accessor, JSXElement, Setter, children, createContext, createEffect, createSignal, onMount, useContext } from "solid-js"; import constants from "@/constants"; type TThemeProviderProps = { children: JSXElement; defaultTheme: string; }; type TThemeContext = [Accessor<string>, Setter<string>]; const ThemeKey = "stk-auth-theme"; const ThemeProvider = (props: TThemeProviderProps) => { const c = children(() => props.children); const [theme, setTheme] = createSignal<string>(props.defaultTheme); onMount(() => { // 读取缓存 const cachedTheme = localStorage.getItem(ThemeKey); if (cachedTheme) { setTheme(cachedTheme); } else { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; setTheme(prefersDark ? "dark" : "light") } }); createEffect(() => { document.documentElement.classList.toggle(constants.DARK_MODE, theme() === constants.DARK_MODE); // 写入缓存 localStorage.setItem(ThemeKey, theme()); }); const values: TThemeContext = [theme, setTheme]; return <ThemeContext.Provider value={values}>{c()}</ThemeContext.Provider>; }; export const ThemeContext = createContext<TThemeContext>([() => "", undefined]); export default ThemeProvider; export const useTheme = () => { return useContext(ThemeContext); };
原因分析与修复
核心原因
在Solid.js中,children()函数返回的是响应式访问器,但直接调用c()会强制子元素提前求值,打破了Solid.js的渲染顺序:子组件会在ThemeProvider的Context值(包含setTheme的数组)完成初始化前,就通过useTheme()获取Context,此时拿到的是Context的默认值[() => "", undefined],自然会报错“setTheme不是函数”。
另外,Solid.js中直接使用props.children即可处理绝大多数子元素场景,children()仅用于子元素是动态函数式内容(比如children={(theme) => <div>{theme}</div>})的情况,你的场景完全不需要额外包裹。
修复方案
移除children()的多余使用,直接恢复使用{props.children}即可,修复后代码如下:
import { Accessor, JSXElement, Setter, createContext, createEffect, createSignal, onMount, useContext } from "solid-js"; import constants from "@/constants"; type TThemeProviderProps = { children: JSXElement; defaultTheme: string; }; type TThemeContext = [Accessor<string>, Setter<string>]; const ThemeKey = "stk-auth-theme"; const ThemeProvider = (props: TThemeProviderProps) => { const [theme, setTheme] = createSignal<string>(props.defaultTheme); onMount(() => { const cachedTheme = localStorage.getItem(ThemeKey); if (cachedTheme) { setTheme(cachedTheme); } else { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; setTheme(prefersDark ? "dark" : "light") } }); createEffect(() => { document.documentElement.classList.toggle(constants.DARK_MODE, theme() === constants.DARK_MODE); localStorage.setItem(ThemeKey, theme()); }); const values: TThemeContext = [theme, setTheme]; return <ThemeContext.Provider value={values}>{props.children}</ThemeContext.Provider>; }; export const ThemeContext = createContext<TThemeContext>([() => "", undefined]); export default ThemeProvider; export const useTheme = () => { return useContext(ThemeContext); };
内容的提问来源于stack exchange,提问作者Adharsh M
相关产品推荐
相关产品推荐

