3
votes

I have created a simple React project where I have inserted a Material UI switch in the drawer of my app that toggles the theme of the app from dark to light. The problem is sometimes when I load the app and click the switch, it doesn't respond the first few times. And when I keep clicking, it then changes the theme but the behavior is flipped: the way I made the switch is that the switch is checked/on when it is dark mode and off when it is light, but now, after not responding for the first few times, it changes the behavior and it is on when it is light mode and vice versa.

The way I am toggling the switch is this:

states that I used:

  const [isDark, setDarkMode] = React.useState(true);
  const [theme, setTheme] = React.useState(darkTheme);

toggleTheme function:

const toggleTheme = () => {
    setDarkMode((prev) => !prev);
    setTheme(isDark ? darkTheme : lightTheme);
  };

and here is my switch:

<Switch edge="start" checked={isDark} onChange={toggleTheme} />

EDIT: And this issue still persists if i do this instead:

setTheme((prev) => prev === darkTheme? lightTheme: darkTheme);

Result: (behavior is still flipped)

enter image description here

Code:

enter image description here

My CodeSandbox link: https://codesandbox.io/s/testing-karmapact-app-bghgp?file=/App.js

3

3 Answers

1
votes

You need to invert the theme toggle setTheme(isDark ? darkTheme : lightTheme);

If current is dark theme then you want to set the light them, and vice versa.

const toggleTheme = () => {
  setDarkMode((prev) => !prev);
  setTheme(isDark ? lightTheme : darkTheme);
};
0
votes

This is probably the work of React.StrictMode. It is a currently a bug as of Material-UI Release 4.11.0. In short, you are going to want to take out React.StrictMode to test the switching of themes.

Example of the Bug when using React.StrictMode:

const darkTheme = createMuiTheme({
  palette: {
    type: "dark",
    primary: {
      main: "#ffffff"
    }
  }
});

const lightTheme = createMuiTheme({
  palette: {
    type: "light",
    primary: {
      main: "#000000"
    }
  }
});

function App() {
  const [theme, setTheme] = useState(darkTheme);

  return (
    <ThemeProvider theme={theme}>
      <CssBaseline>
        <ButtonGroup>
          <Button
            variant="contained"
            onClick={() => {
              setTheme(lightTheme);
            }}
          >
            Light
          </Button>
          <Button
            variant="contained"
            onClick={() => {
              setTheme(darkTheme);
            }}
          >
            Dark
          </Button>
        </ButtonGroup>

        <Typography color="primary">
          Lorem Ipsum is simply dummy text of the printing and typesetting
          industry. Lorem Ipsum has been the industry's standard dummy text ever
          since the 1500s, when an unknown printer took a galley of type and
          scrambled it to make a type specimen book. It has survived not only
          five centuries, but also the leap into electronic typesetting,
          remaining essentially unchanged. It was popularised in the 1960s with
          the release of Letraset sheets containing Lorem Ipsum passages, and
          more recently with desktop publishing software like Aldus PageMaker
          including versions of Lorem Ipsum.
        </Typography>
      </CssBaseline>
    </ThemeProvider>
  );
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.querySelector("#root")
);
<body>
  <div id="root"></div>

  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script src="https://unpkg.com/@material-ui/[email protected]/umd/material-ui.development.js"></script>
  <script type="text/babel">
    const {
      createMuiTheme,
      ThemeProvider,
      Typography,
      Button,
      ButtonGroup,
      CssBaseline
    } = MaterialUI;
    const { useState } = React;
  </script>
</body>

As an alternative solution in addition to taking out React.StrictMode, it seems to work as well when you shallowly clone theme, e.g., <ThemeProvider theme={{...theme}}>

const darkTheme = createMuiTheme({
  palette: {
    type: "dark",
    primary: {
      main: "#ffffff"
    }
  }
});

const lightTheme = createMuiTheme({
  palette: {
    type: "light",
    primary: {
      main: "#000000"
    }
  }
});

function App() {
  const [theme, setTheme] = useState(darkTheme);

  return (
    <ThemeProvider theme={{...theme}}>
      <CssBaseline>
        <ButtonGroup>
          <Button
            variant="contained"
            onClick={() => {
              setTheme(lightTheme);
            }}
          >
            Light
          </Button>
          <Button
            variant="contained"
            onClick={() => {
              setTheme(darkTheme);
            }}
          >
            Dark
          </Button>
        </ButtonGroup>

        <Typography color="primary">
          Lorem Ipsum is simply dummy text of the printing and typesetting
          industry. Lorem Ipsum has been the industry's standard dummy text ever
          since the 1500s, when an unknown printer took a galley of type and
          scrambled it to make a type specimen book. It has survived not only
          five centuries, but also the leap into electronic typesetting,
          remaining essentially unchanged. It was popularised in the 1960s with
          the release of Letraset sheets containing Lorem Ipsum passages, and
          more recently with desktop publishing software like Aldus PageMaker
          including versions of Lorem Ipsum.
        </Typography>
      </CssBaseline>
    </ThemeProvider>
  );
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.querySelector("#root")
);
<body>
  <div id="root"></div>

  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script src="https://unpkg.com/@material-ui/[email protected]/umd/material-ui.development.js"></script>
  <script type="text/babel">
    const {
      createMuiTheme,
      ThemeProvider,
      Typography,
      Button,
      ButtonGroup,
      CssBaseline
    } = MaterialUI;
    const { useState } = React;
  </script>
</body>
0
votes

The problem is probably with your function:

const toggleTheme = () => {
  setDarkMode((prev) => !prev);
  setTheme(isDark ? darkTheme : lightTheme);
};

It should be as following:

const toggleTheme = () => {
  setDarkMode((prev) => !prev);
  setTheme(isDark ? lightTheme : darkTheme); // this condition should be changed
};