【问题标题】:React Material UI Theme ChangeReact Material UI 主题更改
【发布时间】:2019-09-14 17:35:32
【问题描述】:

您能帮我动态更改 React Material UI 主题吗?

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

我尝试通过在按钮单击时更改主题属性。如控制台所示,主题属性正在发生变化。但变化并没有体现在主题上。

沙盒代码: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")
);

当我点击按钮时,主题必须更改为深色

【问题讨论】:

    标签: reactjs material-ui


    【解决方案1】:

    我正在使用styledComponentstypescriptmaterial-ui

    首先我定义了我的主题:

    // 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,
      },
    });
    

    我定义了一个 themeProvider 函数,在这个函数中,我将 material-ui 的 ThemeProvider 包装在一个 React 上下文中,以便能够轻松更改主题:

    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;
    

    现在我可以像这样在我的组件中使用它:

    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>
        );
    }
    

    在主题之间切换:

    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>
        );
    }
    

    【讨论】:

      【解决方案2】:

      我正在使用 Material UI v4。

      我尝试了类似 Ashkan 的回答,但对我不起作用。

      但是,我在文档中找到了this,并将其抽象为适用于不同的状态,而不是用户偏好,这对我有用。对于您的示例,我可能会创建一个上下文:

      // context.js
      import React, { useContext } from "react";
      import { ThemeProvider, createTheme } from "@material-ui/core/styles";
      import CssBaseline from "@material-ui/core/CssBaseline";
      
      const CustomThemeContext = React.createContext();
      
      // You can add more to these and move them to a separate file if you want.
      const darkTheme = {
        palette: {
          type: "dark",
        }
      }
      const lightTheme = {
        palette: {
          type: "light",
        }
      }
      
      export function CustomThemeProvider({ children }) {
        const [dark, setDark] = React.useState(false);
      
        function toggleTheme() {
          if (dark === true) {
            setDark(false);
          } else {
            setDark(true);
          }
        }
      
        const theme = React.useMemo(
          () => {
            if (dark === true) {
              return createTheme(darkTheme);
            }
            return createTheme(lightTheme);
          },
          [dark],
        );
      
        return (
          <CustomThemeContext.Provider value={toggleTheme}>
            <ThemeProvider theme={theme}>
              <CssBaseline />
              {children}
            </ThemeProvider>
          </CustomThemeContext.Provider>
        );
      }
      
      export function useToggleTheme() {
        const context = useContext(CustomThemeContext);
        if (context === undefined) {
          throw new Error("useCustomThemeContext must be used within an CustomThemeProvider");
        }
        return context;
      }
      

      然后将您的应用包装在其中:

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

      然后在您的应用中访问它:

      export default function App(){
        const toggleTheme = useToggleTheme();
      
        return (
          <div>
            <button onClick={toggleTheme}>Toggle the theme!!</button>
          </div>
        );
      }
      

      附带说明一下,在我的应用程序中,我实际上在应用程序的两个部分中有不同的主题,具体取决于用户是否登录。所以我只是这样做:

      
      function App() {
        const { authState } = useAuthContext();
      
        const theme = React.useMemo(
          () => {
            if (authState.user) {
              return createTheme(dashboardTheme);
            }
            return createTheme(loginTheme);
          },
          [authState.user],
        );
      
        return (
          <ThemeProvider theme={theme}>
            <TheRestOfTheApp />
          </ThemeProvider>
      }
      

      您似乎可以通过在 useMemo 中引用它们并将它们包含在依赖项数组中来将主题基于任何状态或多个状态。

      编辑: 我刚刚注意到 MUI v5 实际上在他们的文档中有something very similar

      【讨论】:

        【解决方案3】:

        在您的代码中,主题类型已更改。但是页面不会使用新主题重新渲染。

        我已更改 index.js 和 App.js 中的代码,如下所示。 试试这个方法。它有效。

        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;
        

        【讨论】:

        • 考虑需要在子组件上更改主题类型的情况,而不是在应用程序内部。你想如何解决这个问题?
        猜你喜欢
        • 2019-02-06
        • 1970-01-01
        • 2015-12-26
        • 2020-05-04
        • 2021-11-14
        • 2020-07-05
        • 1970-01-01
        • 2019-10-07
        • 2020-03-27
        相关资源
        最近更新 更多