I have some trouble implementing multiple color themes into my website. I have made a button connected to a state toggle, and every time the state switches, a useEffect handler sets the value of my context.provider to dark/light theme. When I log the useEffect handler it changes the theme, however the child components are not affected by it.
This is my App.tsx:
import {
useContext,
useState,
Provider,
createContext,
useEffect,
} from "react";
import logo from "./logo.svg";
import "./App.scss";
import Nav from "./components/ui/nav/Nav";
import Welcome from "./components/ui/welcome/Welcome";
import Section from "./components/ux/section/Section";
import Prices from "./components/ui/prices/Prices";
import { themes, ThemeContext } from "./components/theme/theme";
import Background from "./components/ui/background/Background";
function App() {
const theme = useContext(ThemeContext);
const [darkmode, setdarkmode] = useState(true);
const themeMode = themes.light;
useEffect(() => {
const themeMode = darkmode ? themes.light : themes.dark;
console.log(themeMode);
}, [darkmode]);
document.body.style.background = theme.primary;
document.body.style.color = theme.text;
return (
<ThemeContext.Provider value={themeMode}>
<button
onClick={() => {
setdarkmode(!darkmode);
}}
>
hello
</button>
<Background />
<Section content={<Welcome />} />
<Section content={<Prices />} />
</ThemeContext.Provider>
);
}
export default App;
And this is one of my components that should use the updated theme:
import React, { useContext, useState } from "react";
import "./button.scss";
import { themes, ThemeContext } from "../../theme/theme";
export default function Button(props: {
invert: boolean;
link: string | URL;
content: string;
}) {
var style = {};
const theme = useContext(ThemeContext);
const buttonStyle = {
color: theme.primary,
background: theme.cta,
border: "solid 2px " theme.cta,
};
const invertStyle = {
color: theme.cta,
background: theme.primary,
border: "solid 2px " theme.cta,
};
props.invert ? (style = invertStyle) : (style = buttonStyle);
const [colorStyle, setColorStyle] = useState(false);
colorStyle
? props.invert
? (style = buttonStyle)
: (style = invertStyle)
: props.invert
? (style = invertStyle)
: (style = buttonStyle);
return (
<button
onClick={() => {
window.location.assign(props.link);
}}
onm ouseEnter={() => {
setColorStyle(!colorStyle);
}}
onm ouseLeave={() => {
setColorStyle(!colorStyle);
}}
className="ux-btn"
style={style}
>
{props.content}
</button>
);
}
CodePudding user response:
You have const themeMode = themes.light;
in your component. This does exactly what it says: sets the themeMode
variable to be the constant themes.light
.
When you attempt (I presume) to "change it" in the event handler, you're doing const themeMode = darkmode ? themes.light : themes.dark;
, but with nothing else in that function (other than a console.log
) - as this variable is only scoped to that one function, it does not change the same-named variable outside.
Even if you made it change the outer variable (by declaring the outer variable with let
instead of const
and not shadowing the variable inside the function but updating its value), that won't work at all well with React.
The solution is to use state instead:
- replace
const themeMode = themes.light
withconst [themeMode, setThemeMode] = useState(themes.light);
- inside the event handler, replace
const themeMode = darkmode ? themes.light : themes.dark;
withsetThemeMode(darkmode ? themes.light : themes.dark);
Making the above 2 changes will work - but you can still simplify your component quite a lot. You don't need both darkMode
and themeMode
in state, as one is simply calculated from the other. If you just use one state variable you also won't need a useEffect
to automatically update one based on the other. But I'll leave you to sort out those problems - I hope this helps you get started, at least!