I am trying to incorporate a dark mode on all my pages in a Next JS project, using Material UI for styling and useContext with useReducer hooks to set dark state. I placed both context provider and material theme provider in the _app.js above all the rest of the code so that every page gets the same theme and context. Header component has the switch mode button and is reading state correctly. But the theme doesn't change. It is always as I define it in the initial conditions in useReducer within useContext. Here is the _app.js code:
function MyApp({ Component, pageProps, props }) {
const ctx = useContext(ThemeContext);
const { darkMode, setDarkMode } = ctx;
useEffect(() => {
console.log(darkMode);
}, [darkMode]);
useEffect(() => {
const theme = localStorage.getItem('pfst-theme');
if (theme) {
if (theme === 'dark') {
setDarkMode(true);
} else {
setDarkMode(false);
}
} else {
localStorage.setItem('pfst-theme', 'light');
setDarkMode(true);
}
}, []);
return (
<ThemeContextProvider>
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
<CssBaseline>
<Box id="back-to-top-anchor" />
<ScrollToColor02 {...props}>
<AppBar>
<Header />
</AppBar>
</ScrollToColor02>
<Component {...pageProps} />
<Footer />
<ScrollTop {...props}>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
</CssBaseline>
</ThemeProvider>
</ThemeContextProvider>
);
}
On a side note, console log in useEffect in _app.js logs only on first render and not when dark mode state changes.
Here is the context code:
import { createContext, useReducer } from 'react';
export const ThemeContext = createContext({
setDarkMode: (val) => {},
darkMode: false,
});
const reducer = (state, action) => {
switch (action.type) {
case 'SET_THEME':
return {
...state,
darkMode: action.payload,
};
default:
return state;
}
};
const initialState = {
darkMode: false,
};
export const ThemeContextProvider = (props) => {
const [state, dispatch] = useReducer(reducer, initialState);
const setDarkMode = (val) => {
dispatch({
type: 'SET_THEME',
payload: val,
});
};
return <ThemeContext.Provider value={{
darkMode: state.darkMode, setDarkMode}}>
{props.children}</ThemeContext.Provider>;
};
Does anyone have any idea why dark mode never changes in the _app, but changes in other components (i.e. header, where the switch mode button label changes depending on dark mode state)?
If there is something more you require, let me know.