【问题标题】:How to get theme from store and switch the app theme on react-admin?如何从商店获取主题并在 react-admin 上切换应用主题?
【发布时间】:2020-09-18 18:33:39
【问题描述】:

react-admin 版本:3.8.4

我有一个 react-admin 应用,我正在尝试在浅色和深色主题之间切换。

您可以在下面看到 Theme.js,我在其中导出了两个具有默认主题覆盖的对象,如文档中所述。 (https://material-ui.com/pt/customization/default-theme/)

export const darkTheme = {
palette: {
    type: 'dark'
},
overrides: {
    MuiAppBar: {
        colorSecondary: {
            backgroundColor: '#424242', //'#1e4c9a',
            color: '#fff'
        },
    },
    MuiButton: {
        textPrimary: {
            color: '#fff',
        }
    },
    MuiTypography: {
        colorPrimary: {
            color: '#fff'
        }
    },
    MuiDrawer: {
        paper: {
            paddingTop: '20px'
        }
    },
    MuiFilledInput: {
        underline: {
            '&:after': {
                borderBottomColor: '#bf9f00'
            }
        }
    },
    MuiFormLabel: {
        root: {
            '&$focused': {
                color: '#bf9f00'
            }
        }
    },
}
}

export const lightTheme = {
    palette: {
        type: 'light'
    },
    overrides: {
        MuiAppBar: {
            colorSecondary: {
                backgroundColor: '#2196f3',
                color: '#fff'
            },
        },
    },
}

我还做了一个 customReducer,如下所示,名为 ThemeReducer.js

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

import { darkTheme } from '../../layout/Theme'
const Theme = createMuiTheme(darkTheme)

export default (previousState = Theme, action) => {
  if (action.type === 'SWITCH_THEME') {
    return action.theme;
  }
  return previousState;
}

还有一个动作来调度状态(ThemeAction.js):

export const switchTheme = (theme) => {
  return {
    type: 'SWITCH_THEME',
    theme
  }
}

要设置自定义 Reducer,如文档中所述,我在 <Admin> 上设置了自定义 Reducer 属性,如下所示:

import Theme from './store/reducers/themeReducer'


const App = () => {

  return (
    <div className="admin-body">

      <Admin
        customReducers={{ theme: Theme }}
        layout={Layout}
        authProvider={login}
        dataProvider={dataProvider}
        loginPage={LoginPage}
        locale="pt"
        customRoutes={[
          <Route
            key="configuration"
            path="/configuration"
            title="Configurações"
            component={Configuration}
          />
        ]}
      > ...

要在主题之间切换并设置到我正在使用此配置页面的商店:

import React from 'react'
import { useSelector, useDispatch } from 'react-redux';
import Button from '@material-ui/core/Button';
import { switchTheme } from '../../store/actions/themeAction';
import { darkTheme, lightTheme } from '../../layout/Theme'
import { createMuiTheme } from '@material-ui/core/styles';

import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import { Title } from 'react-admin';

import './styles.css'

const Configuration = () => {
  const dispatch = useDispatch();
  const theme = useSelector(state => state.theme);

  const mainClass = 'configPage'

  return (
    <Card>
      <Title title="Configurações" />
      <CardContent className={mainClass}>
        <div className="theme-label">
          <p className="text" >Selecione seu Tema: </p>
          <Button
            variant="contained"
            className={theme.palette.type === 'light' ? 'active' : ''}
            onClick={() => dispatch(switchTheme(createMuiTheme(lightTheme)))}
          >
            Claro
        </Button>
          <Button
            variant="contained"
            className={theme.palette.type === 'dark' ? 'active' : ''}
            onClick={() => dispatch(switchTheme(createMuiTheme(darkTheme)))}
          >
            Escuro
        </Button>
        </div>
      </CardContent>
    </Card>

  )
}

export default Configuration;

所有这些结构都工作正常,我的意思是全局状态主题正在正确更新,因此reducer 正在获取点击的主题并切换状态。

问题在于,如文档中所述,要更改默认主题,我们必须将新主题作为标签 &lt;Admin&gt; 上的属性传递,例如:&lt;Admin theme={theme}&gt; &lt;/admin&gt; 但是标签&lt;Admin&gt;是在App.js中设置的,redux上下文不在App之上,所以无法获取全局主题状态。

那么我的问题是如何使用我创建的全局主题状态作为应用程序主题传递。

如下所示,我已经尝试将其作为 Layout 的父级传递,但主题并未反映在应用程序上。

Layout.js

const MyLayout = (props) => {
    const theme = useSelector(state => state.theme)
    return (
        <ThemeProvider theme={theme}>
            <Layout
                {...props}
                appBar={AppBar}
                sidebar={Sidebar}
                menu={Menu}
                notification={Notification}
            />
        </ThemeProvider>
    )
};

App.js

...
    import Layout from './layout'
    
    const App = () => {
    
      return (
        <div className="admin-body">
    
          <Admin
            customReducers={{ theme: Theme }}
            layout={Layout}
            authProvider={login}
            dataProvider={dataProvider}
            loginPage={LoginPage}
            locale="pt"
            customRoutes={[
              <Route
                key="configuration"
                path="/configuration"
                title="Configurações"
                component={Configuration}
              />
            ]}
          >
...

感谢任何帮助。 问候

【问题讨论】:

    标签: material-ui react-admin theming themeprovider


    【解决方案1】:

    我不知道你的要求,但也许没有必要将主题存储在 redux 中?您可以使用使用反应上下文 api 的解决方案,如下所示:

    type ThemeState = {
        theme: 'light' | 'dark',
        setTheme(theme: 'light' | 'dark'): void,
    };
    
    const StateContext = React.createContext<ThemeState>({
        theme: 'light',
        setTheme: () => {}
    });
    
    export const ThemeStateProvider = (props: { children: React.ReactNode }) => {
    
        const [theme, setTheme] = React.useState('light');
    
        return (
    
            <StateContext.Provider value={{
                theme,
                setTheme
            }}>
                {props.children}
            </StateContext.Provider>
        );
    };
    
    export const useThemeState = () => useContext(StateContext);
    

    然后像这样使用它:

    // new root compontent that wraps App
    const Root = () => (
        <ThemeStateProvider>
           <App />
        </ThemeStateProvider>
    )
    
    const App = () => {
        const {theme} = useThemeState();
       
        return <Admin theme={theme === 'light' ? lightTheme : darkTheme} ... />
    }
    
    const Configuration = () => {
        const {setTheme} = useThemeState();
       
        [...]
    
        return (
            [...]
            <Button onClick={() => setTheme('light')} />
            [...]
        );
    }
    

    希望对你有用!

    【讨论】:

    • 一个更简单的解决方案,我当时没有想到。感谢您的帮助,效果很好!
    • 太棒了。谢谢。
    猜你喜欢
    • 2020-09-02
    • 2021-09-10
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 1970-01-01
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多