2

反应管理员版本: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
  }
}

要设置自定义减速器,如文档中所述,我在<Admin>属性自定义减速器上设置如下:

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;

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

问题是,如文档中所述,要更改默认主题,我们必须将新主题作为标签上的属性传递,<Admin>例如:<Admin theme={theme}> </admin> 但是标签<Admin>被设置到 App.js 中,并且 redux 上下文不在应用程序,因此无法将全局主题状态放入其中。

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

正如您在下面看到的,我已经尝试将其作为 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>
    )
};

应用程序.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}
              />
            ]}
          >
...

感谢任何帮助。问候

4

1 回答 1

6

我不知道您的要求,但也许没有必要将主题存储在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-19T09:16:26.930 回答