1
votes

can you please help me to change the React Material UI theme Dynamically .

https://imgur.com/S8zsRPQ https://imgur.com/Ul8J40N

I have tried by changing the theme Properties on button click . The theme properties are getting changed as seen in the console . But the change is not reflecting on the theme .

Sandbox Code : https://codesandbox.io/s/30qwyk92kq

const themeChange = () => {
  alert(theme);
  theme.palette.type = "light";
  console.log(theme);
};
ReactDOM.render(
  <MuiThemeProvider theme={theme}>
    <React.Fragment>
      <CssBaseline />
      <App changeTheme={themeChange} />
    </React.Fragment>
  </MuiThemeProvider>,
  document.getElementById("app")
);

When I click the button the theme has to change to Dark color

2

2 Answers

3
votes

I am using styledComponents, typescript and material-ui.

First I defined my themes:

// This is my dark theme: dark.ts
// I defined a light.ts too
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';

export const darkTheme = createMuiTheme({
  palette: {
    type: 'dark',   // Name of the theme
    primary: {
      main: '#152B38',
    },
    secondary: {
      main: '#65C5C7',
    },
    contrastThreshold: 3,
    tonalOffset: 0.2,
  },
});

I defiend a themeProvider function and in this function I wrapped the material-ui's ThemeProvider in a React context to be able to change the theme easily:

import React, { useState } from 'react';
import {ThemeProvider} from "@material-ui/core/styles/";
import { lightTheme } from "./light";
import { darkTheme } from "./dark";

const getThemeByName = (theme: string) => {
  return themeMap[theme];
}

const themeMap: { [key: string]: any } = {
  lightTheme,
  darkTheme
};

export const ThemeContext = React.createContext(getThemeByName('darkTheme'));

const ThemeProvider1: React.FC = (props) => {
  // State to hold the selected theme name
  const [themeName, _setThemeName] = useState('darkTheme');

  // Retrieve the theme object by theme name
  const theme = getThemeByName(themeName);

  return (
        <ThemeContext.Provider value={_setThemeName}>
            <ThemeProvider theme={theme}>{props.children}</ThemeProvider>
        </ThemeContext.Provider>
    );
}
export default ThemeProvider1;

Now I can use it in my components like this:

import React from 'react';
import styled from 'styled-components';
import useTheme from "@material-ui/core/styles/useTheme";

const MyCardHeader = styled.div`
                        width: 100%;
                        height: 40px;
                        background-color: ${props => props.theme.bgColor}; 
                        color: ${props => props.theme.txtColor}; 
                        display: flex;
                        align-items:center;
                        justify-content: center;
                    `;

export default function CardHeader(props: { title: React.ReactNode; }) {
    const theme = {
        bgColor: useTheme().palette.primary.main, 
        txtColor: useTheme().palette.primary.contrastText
    };

    return (
        <MyCardHeader theme={theme}>
            {props.title}
        </MyCardHeader>
    );
}

For Changing between themes:

import React, {useContext} from 'react';
import { ThemeContext} from './themes/themeProvider';


export default function Header() {
    // Get the setter function from context
    const setThemeName = useContext(ThemeContext);
    
    return (
        <header>
            <button onClick={() => setThemeName('lightTheme')}>
                light
            </button>
            <button onClick={() => setThemeName('darkTheme')}>
                dark
            </button>
        </header>
    );
}
0
votes

In your code, theme type is changed. But the Page is not re-rendered with new theme.

I have changed code in index.js and App.js like following. Try this approach. It works.

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(
  <App/>,
  document.getElementById("app")
);

App.js

import React from "react";
import CssBaseline from "@material-ui/core/CssBaseline";
import Typography from "@material-ui/core/Typography";
import { Button } from "@material-ui/core";
import { MuiThemeProvider, createMuiTheme } from "@material-ui/core/styles";
import blueGrey from "@material-ui/core/colors/blueGrey";
import lightGreen from "@material-ui/core/colors/lightGreen";

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      themeType : 'dark',
    }
  }

  changeTheme(){
    if (this.state.themeType == 'dark'){
      this.setState({themeType:'light'});
    } else {
      this.setState({themeType:'dark'});
    }
  }
  render() {
    let theme = createMuiTheme({
      palette: {
        primary: {
          light: lightGreen[300],
          main: lightGreen[500],
          dark: lightGreen[700]
        },
        secondary: {
          light: blueGrey[300],
          main: blueGrey[500],
          dark: blueGrey[700]
        },
        type: this.state.themeType
      }
    });
    return (
      <MuiThemeProvider theme={theme}>
        <CssBaseline />
        <Typography>Hi there!</Typography>
        <Button
          variant="contained"
          color="secondary"
          onClick={()=>{this.changeTheme()}}
        >
          Change
        </Button>
      </MuiThemeProvider>
    );
  }
}
export default App;