S
SolidJS•16mo ago
hotshoe

contextProvider using signal

Hi, noob question here -- just getting feet wet w/ SolidStart. Any help is appreciated!! Trying to create a context provider for setting theme, and prop is getting passed down but child comps not reacting.
// theme-provider.tsx
import {
createContext,
useContext,
ParentComponent,
createSignal,
} from "solid-js";

const defaultTheme = "light";

export type ThemeContextValue = [get: string, set: (theme: string) => void];
const ThemeContext = createContext<ThemeContextValue>([
defaultTheme,
(theme: string) => {},
]);

export const ThemeProvider: ParentComponent<{
theme?: string;
}> = (props) => {
const [theme, setter] = createSignal(defaultTheme);
const setTheme = (val: string) => {
console.log("ThemeProvider: setTheme", val);
setter(val);
};

return (
// Note: if I put el here and bind to theme value, // it is reactive when called using setter from // child comp, so seems everything is working here.
<ThemeContext.Provider value={[theme(), setTheme]}>
{props.children}
</ThemeContext.Provider>
);
};

export const useTheme = () => useContext(ThemeContext);
// theme-provider.tsx
import {
createContext,
useContext,
ParentComponent,
createSignal,
} from "solid-js";

const defaultTheme = "light";

export type ThemeContextValue = [get: string, set: (theme: string) => void];
const ThemeContext = createContext<ThemeContextValue>([
defaultTheme,
(theme: string) => {},
]);

export const ThemeProvider: ParentComponent<{
theme?: string;
}> = (props) => {
const [theme, setter] = createSignal(defaultTheme);
const setTheme = (val: string) => {
console.log("ThemeProvider: setTheme", val);
setter(val);
};

return (
// Note: if I put el here and bind to theme value, // it is reactive when called using setter from // child comp, so seems everything is working here.
<ThemeContext.Provider value={[theme(), setTheme]}>
{props.children}
</ThemeContext.Provider>
);
};

export const useTheme = () => useContext(ThemeContext);
(cont'd)
16 Replies
hotshoe
hotshoe•16mo ago
Then boiler plate root.tsx, wrapped in ThemeProvider. The theme is getting passed and UI reflects the change.
// root.tsx
// @refresh reload
import { createSignal, onMount, Suspense } from "solid-js";
import {
A,
Body,
ErrorBoundary,
FileRoutes,
Head,
Html,
Meta,
Routes,
Scripts,
Title,
} from "solid-start";
import { ThemeProvider, useTheme } from "./providers/theme-provider";
import "./root.css";

export default function Root() {
const [theme, setTheme] = useTheme();
return (
<ThemeProvider>
<Html lang="en" data-theme={theme}>
<Head>
<Title>SolidStart + AuthJS</Title>
<Meta charset="utf-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Body>
<Suspense>
<ErrorBoundary>
<A href="/">Home</A>
<A href="/protected">Protected</A>
<Routes>
<FileRoutes />
</Routes>
</ErrorBoundary>
</Suspense>
<Scripts />
</Body>
</Html>
</ThemeProvider>
);
}
// root.tsx
// @refresh reload
import { createSignal, onMount, Suspense } from "solid-js";
import {
A,
Body,
ErrorBoundary,
FileRoutes,
Head,
Html,
Meta,
Routes,
Scripts,
Title,
} from "solid-start";
import { ThemeProvider, useTheme } from "./providers/theme-provider";
import "./root.css";

export default function Root() {
const [theme, setTheme] = useTheme();
return (
<ThemeProvider>
<Html lang="en" data-theme={theme}>
<Head>
<Title>SolidStart + AuthJS</Title>
<Meta charset="utf-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Body>
<Suspense>
<ErrorBoundary>
<A href="/">Home</A>
<A href="/protected">Protected</A>
<Routes>
<FileRoutes />
</Routes>
</ErrorBoundary>
</Suspense>
<Scripts />
</Body>
</Html>
</ThemeProvider>
);
}
However, if I call the setter with a different theme, the UI is not reacting to the new value. I'm sure the setter is getting because value updates from within theme-provider.tsx . This is so basic that I'm clearly doing something wrong, but I'm two hours in and making no progress -- hoping somebody can shed some light.
lxsmnsyc
lxsmnsyc•16mo ago
value={[theme(), setTheme]} This one is basically a mistake. Context Provider's value prop isn't reactive so what you pass here will never change. It's better if you pass theme as-is In this case instead of string it would be () => string so value={[theme, setTheme]}
hotshoe
hotshoe•16mo ago
Thanks you so much for the quick response! Let me give a try and let it sink in -- I'll get back -- Super appreciate help!!! @lxsmnsyc , ty! that did the trick (jsut needed to fix the type error since accessor instead of the val). Diff issue though, inner components are updating (I put test elem in body reflecting theme val), but data-theme attrib on <html> tag val not reacting to state change. any thought there? Sorry to have to ask 😕 but you got me so close now.
lxsmnsyc
lxsmnsyc•16mo ago
might be getting the default context value, which is a bug in HMR. Have you tried refreshing the page?
hotshoe
hotshoe•16mo ago
y, refreshed. no luck
lxsmnsyc
lxsmnsyc•16mo ago
ah you meant the html
hotshoe
hotshoe•16mo ago
y
lxsmnsyc
lxsmnsyc•16mo ago
I'm not sure if html attributes are reactive though also try calling it theme()
hotshoe
hotshoe•16mo ago
still no luck
lxsmnsyc
lxsmnsyc•16mo ago
what does your root look like now? also you're calling const [theme, setTheme] = useTheme(); outside ThemeProvider?
hotshoe
hotshoe•16mo ago
let me do experiment to see if html reactive or now -- will get back in a few -- ty!
lxsmnsyc
lxsmnsyc•16mo ago
calling it outside means useTheme will always return the default context value
hotshoe
hotshoe•16mo ago
verified html el is reacting ok w/ some test code.
// root.tsx
...
export default function Root() {
const [theme, setTheme] = useTheme();
const [val, setVal] = createSignal<string>("light");
return (
<ThemeProvider>
{/*
<Html lang="en" data-theme={theme}> // Fails
<Html lang="en" data-theme={val()}> {/* Works! :-) */}
<Head>
<Title>SolidStart + AuthJS</Title>
<Meta charset="utf-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Body>
<button
onClick={() => {
setVal("dark"); // Works
}}
>
// root.tsx
...
export default function Root() {
const [theme, setTheme] = useTheme();
const [val, setVal] = createSignal<string>("light");
return (
<ThemeProvider>
{/*
<Html lang="en" data-theme={theme}> // Fails
<Html lang="en" data-theme={val()}> {/* Works! :-) */}
<Head>
<Title>SolidStart + AuthJS</Title>
<Meta charset="utf-8" />
<Meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<Body>
<button
onClick={() => {
setVal("dark"); // Works
}}
>
This is really odd, because in index.tsx, same setup and calling pattern, but works
// index.tsx
import { useTheme } from "~/providers/theme-provider";
...
export default function Home() {
const [theme, setTheme] = useTheme();
....

return (
<main>
<h1>{theme}</h1> {/* Works!! */}
// index.tsx
import { useTheme } from "~/providers/theme-provider";
...
export default function Home() {
const [theme, setTheme] = useTheme();
....

return (
<main>
<h1>{theme}</h1> {/* Works!! */}
lxsmnsyc
lxsmnsyc•16mo ago
@hotshoe
const [theme, setTheme] = useTheme();
return (
<ThemeProvider>
const [theme, setTheme] = useTheme();
return (
<ThemeProvider>
the flow is wrong
hotshoe
hotshoe•16mo ago
Agggggggh!!!!! Ty!! Sorry for the bother., but super appreciated!!
Unknown User
Unknown User•16mo ago
Message Not Public
Sign In & Join Server To View