-2

I followed many solutions like this https://stackoverflow.com/a/71081567/19460332 and the official solution for this https://github.com/mui/material-ui/blob/master/examples/nextjs but nothing worked for me. I am using nextjs , material ui and js-cookies

the issue I am facing is for the MUI Switch

have a look to my code here https://github.com/MaheshYadavGitHub/pizza-gallery:

Here is the code for the section that is breaking :

below is the Layout.js where the Switch to toggle darkMode is not working as expected. The darkTheme gets applied as it gets from the cookies but the switch button still stays in off position on the first render but after another render it works perfectly and when again manually refreshed it does the same behaviour :-

import { useState, useContext } from "react";
import {
  Container,
  AppBar,
  Toolbar,
  Typography,
  Link,
  Switch,
  CssBaseline,
  ThemeProvider,
  createTheme,
} from "@material-ui/core";
import { red } from "@material-ui/core/colors";
import Head from "next/head";
import Image from "next/image";
import NextLink from "next/link";
import useStyles from "../utils/styles";
import { Store } from "../utils/Store";
import Cookies from "js-cookie";

const Layout = ({ title, children }) => {
  const { state, dispatch } = useContext(Store);
  const { darkMode } = state;
  const classes = useStyles();

  const theme = createTheme({
    palette: {
      type: darkMode ? "dark" : "light",
      primary: {
        main: "#556cd6",
      },
      secondary: {
        main: "#19857b",
      },
      error: {
        main: red.A400,
      },
    },
    typography: {
      fontFamily: ["Arial", "Roboto Condensed"].join(","),
      fontWeight: 700,
      h1: {
        fontSize: "1.6rem",
        margin: "1rem 0rem",
        fontWeight: 600,
        "@media (min-width:600px)": {
          fontSize: "2.6rem",
        },
      },
      h6: {
        fontWeight: 700,
      },
      navLinks: {
        color: "#fff",
      },
    },
  });

  const handleThemeChange = (event) => {
    dispatch({ type: darkMode ? "DARK_MODE_OFF" : "DARK_MODE_ON" });
    const newThemeMode = !darkMode;
    Cookies.set("darkMode", newThemeMode ? "ON" : "OFF");
  };
  return (
    <>
      <Head>
        <title>{title ? `${title} - Pizza Gallery` : "Pizza Gallery"}</title>
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />

        <AppBar position="static" className={classes.navbar}>
          <Toolbar>
            <NextLink href="/" passHref>
              <Link>
                <Typography
                  variant="h6"
                  className={classes.brand}
                  component="div"
                >
                  Pizza Gallery
                </Typography>
              </Link>
            </NextLink>
            <div className={classes.grow}></div>
            <Switch
              id="switch"
              checked={darkMode}
              onChange={handleThemeChange}
              color="primary"
            ></Switch>
            <NextLink href="/login" passHref>
              <Link>
                <Typography>Login</Typography>
              </Link>
            </NextLink>
            <NextLink href="/cart" passHref>
              <Link>
                <Typography>Cart</Typography>
              </Link>
            </NextLink>
          </Toolbar>
        </AppBar>
        <Container className={classes.main}>{children}</Container>
        <footer className={classes.footer}>
          <Typography>all rights reserved © pizza gallery 2022</Typography>
        </footer>
      </ThemeProvider>
    </>
  );
};

export default Layout;

below is the Store :-

import Cookies from "js-cookie";
import { createContext, useReducer } from "react";

export const Store = createContext();

const initialState = {
  darkMode: Cookies.get("darkMode") === "ON" ? true : false,
};

const reducer = (state, action) => {
  switch (action.type) {
    case "DARK_MODE_ON":
      return { ...state, darkMode: true };
    case "DARK_MODE_OFF":
      return { ...state, darkMode: false };
    default:
      return state;
  }
};

export const StoreProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const value = { state, dispatch };
  return <Store.Provider value={value}>{children}</Store.Provider>;
};

/pages/_app.js

import { useEffect } from "react";
import "../styles/globals.css";
import { StoreProvider } from "../utils/Store";

function MyApp({ Component, pageProps }) {
  useEffect(() => {
    const jssStyles = document.querySelector("#jss-server-side");
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);
  return (
    <StoreProvider>
      <Component {...pageProps} />
    </StoreProvider>
  );
}

export default MyApp;

/pages/_document.js

import { ServerStyleSheets } from "@material-ui/core/styles";
import Document, { Html, Head, Main, NextScript } from "next/document";
import React from "react";

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head></Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

MyDocument.getInitialProps = async (ctx) => {
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;
  ctx.renderPage = () => {
    return originalRenderPage({
      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
    });
  };
  const initialProps = await Document.getInitialProps(ctx);
  return {
    ...initialProps,
    styles: [
      ...React.Children.toArray(initialProps.styles),
      sheets.getStyleElement(),
    ],
  };
};

2 Answers2

2

If you're not using Server Side Rendering for that component, you may try importing the component, by turning off ssr:

import dynamic from 'next/dynamic';
const Component = dynamic(() => 
    import('../components/path_to_component'), {
        ssr: false,
    });

Hope this helps!

Farid Shabanov
  • 206
  • 3
  • 11
  • Sir, while implementing the dark mode, I was also stressed by the problem of className returning to its original state. So while I was looking around the posts, there was a gold mine here. Thank you. Thanks to you, we've solved problem. – spec Jul 13 '22 at 14:05
0

Kindly ensure that the page is mounted before rendering the client. You can use useState to set a variable like const [mount, setMount]= useState(false).

And then use useEffect to change the default value when the page loads.

    useEffect(() => {
      setMount(true)

     }, [])

Thereafter, you return your page like this.

return(<>
{mount && (
//then display your code here
)}
</>)