How do I force Update my components in my react code to toggle dark theme to light theme

Viewed 857

I'm trying to toggle between light and dark theme in react using a Switch from material-ui. It is happening only once in which it is toggling from dark to light theme. This is the link to the code sandbox where all the code is hosted.

With the help of console logs I can see the theme changing in console but react is not updating it.

I know react state updates are asynchronous but what is the work around I can do for this?

2 Answers

Here is a working code:

import React, { useState } from "react";
import { ThemeProvider, CssBaseline } from "@material-ui/core";
import darkTheme from "./Theme/darkTheme";
import lightTheme from "./Theme/lightTheme";
import PrimarySearchAppBar from "./Components/Appbar";

import { createMuiTheme } from "@material-ui/core/styles";

function App() {
  const [dark, setDark] = useState(true);
  const Theme = dark ? createMuiTheme(darkTheme) : createMuiTheme(lightTheme);
  return (
    <ThemeProvider theme={Theme}>
      <CssBaseline />
      <div>
        <PrimarySearchAppBar thm={dark} Togglethm={setDark} />
      </div>
    </ThemeProvider>
  );
}

export default App;

Please note the two lines that are different from your code:

import { createMuiTheme } from "@material-ui/core/styles";
...
...
const Theme = dark ? createMuiTheme(darkTheme) : createMuiTheme(lightTheme);

It appears that for the themes to work MUI needs to create the theme (using createMuiTheme on every render). I do realize you're using createMuiTheme in your theme definitions (darkTheme.js and lightTheme.js), but those are superfluous. You can safely rewrite those files like so (which is what I did in my sandbox):

const darkTheme = {
  palette: {
    type: "dark",
    primary: {
      main: "#00e676"
    },
    secondary: {
      main: "#e6006f"
    }
  }
};

export default darkTheme;

Sandbox: https://codesandbox.io/s/delicate-butterfly-d8jqd?file=/src/App.js

All you have to do is to import and use createMuiTheme instead of using the theme directly and you can export the plain objects from dakTheme.js and lightTheme.js

you have to recreate theme each time the app stat changes (user toggles theme)

to avoid unnecessary reRenders of the ThemeProvider and as a result reRendering of the whole app you should use a useMemo react hook for changing the theme depending on the dark state.

App.js

import React, { useState, useMemo } from "react";
import { ThemeProvider, CssBaseline } from "@material-ui/core";
import darkTheme from "./Theme/darkTheme";
import lightTheme from "./Theme/lightTheme";
import PrimarySearchAppBar from "./Components/Appbar";
import { createMuiTheme } from "@material-ui/core";
function App() {
  const [dark, setDark] = useState(true);
  const Theme = useMemo(() => createMuiTheme(dark ? darkTheme : lightTheme), [dark]);
  return (
    <ThemeProvider theme={Theme}>
      <CssBaseline />
      <div>
        <PrimarySearchAppBar thm={dark} Togglethm={setDark} />
      </div>
    </ThemeProvider>
  );
}

export default App;

dartkTheme.js

const darkTheme = {
  palette: {
    type: "dark",
    primary: {
      main: "#00e676",
    },
    secondary: {
      main: "#e6006f",
    }
  },
}

export default darkTheme;

lightTheme.js

const lightTheme = {
  palette: {
    type: "light",
    primary: {
      main: "#2196f3"
    },
    secondary: {
      main: "#f50057"
    }
  }
};

export default lightTheme;

Related