I am fairly new to react and I’m implementing some new features using Material-UI. I am trying to switch between light and dark themes while the current theme is being locally stored within the browser. I wish to locally store the current theme as the page refreshes. By default, my app loads in the light theme. Before toggling the switch, I verify to see that light theme is in local storage. As I toggle to dark theme, my app changes to its counterpart and the value within my local storage changes just as expected. But when I try to toggle the switch back to light theme, the theme doesn’t change, just my local storage value. I don’t know why.
If someone has a way of tweaking this code to address this problem or suggest a new idea, I greatly appreciate it.
Demo in CodeSandbox
CustomThemeContext.js
import React, { useState, createContext } from 'react'; import { ThemeProvider } from '@material-ui/core/styles'; import getTheme from '../components/MyThemes'; import Paper from '@material-ui/core/Paper'; export const CustomThemeContext = createContext( { currentTheme: 'normalTheme', setTheme: null, }, ); const CustomThemeProvider = (props) => { const { children } = props const currentTheme = localStorage.getItem('appTheme') || 'normalTheme' const [themeName, _setThemeName] = useState(currentTheme) const theme = getTheme(themeName) const setThemeName = (name) => { localStorage.setItem('appTheme', name) _setThemeName(name) } const contextValue = { currentTheme: themeName, setTheme: setThemeName, } const paperStyle = { height: "100vh", } return ( <div> <CustomThemeContext.Provider value={contextValue}> <ThemeProvider theme={theme}> <Paper style={paperStyle}> {children} </Paper> </ThemeProvider> </CustomThemeContext.Provider> </div> ) } export default CustomThemeProvider;
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import CustomThemeProvider from './contexts/CustomThemeProvider'; ReactDOM.render( <React.StrictMode> <CustomThemeProvider> <App /> </CustomThemeProvider> </React.StrictMode>, document.getElementById('root') ); serviceWorker.unregister();
App.js
import React, { useContext } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { CustomThemeContext } from './contexts/CustomThemeProvider'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Tooltip from '@material-ui/core/Tooltip'; import Typography from '@material-ui/core/Typography'; import BurgerButton from './components/BurgerButton'; import StarButton from './components/StarButton'; import Switch from '@material-ui/core/Switch'; import Button from '@material-ui/core/Button'; import AddCircleIcon from '@material-ui/icons/AddCircle'; import FormControlLabel from '@material-ui/core/FormControlLabel'; const useStyles = makeStyles((theme) => ({ root: { flexGrow: 1, width: 'auto', }, title: { flexGrow: 1, }, content: { flexGrow: 1, padding: theme.spacing(3), } })); export default function App() { const classes = useStyles(); const { currentTheme, setTheme } = useContext(CustomThemeContext); const isDark = Boolean(currentTheme === 'darkTheme') const handleThemeChange = (event) => { const { checked } = event.target if (checked) { setTheme('darkTheme') } else { setTheme('normalTheme') } } /* switch (checked) { case 'darkTheme': return setTheme('darkTheme'); case 'normalTheme': return setTheme('normalTheme'); default: return null; } */ return ( <div className="App"> <div className={classes.root}> <AppBar position="static" title="Memo App"> <Toolbar> <BurgerButton /> <Typography variant="h6" className={classes.title}> Memo App </Typography> <Tooltip title="Toggle light theme/dark theme"> <FormControlLabel control={<Switch checked={isDark} onChange={handleThemeChange} />} label="Theme" /> </Tooltip> <StarButton /> </Toolbar> </AppBar> <Button> <AddCircleIcon /> </Button> </div> </div> ); }
MyThemes.js
import normalTheme from './themes/normalTheme'; import darkTheme from './themes/darkTheme'; const themes = { normalTheme, darkTheme, } export default function getTheme(theme) { return themes[theme] }
Advertisement
Answer
On your ThemeProvider
component theme
prop, clone the theme object you assign to it and it should work
<ThemeProvider theme={{...theme}}> <Paper style={paperStyle}> {children} </Paper> </ThemeProvider>