如何从商店获取主题并在react-admin上切换应用程序主题?

Ria*_*res 2 theming material-ui react-admin themeprovider

反应管理版本:3.8.4

\n

我有一个反应管理应用程序,我正在尝试在浅色和深色主题之间切换。

\n

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

\n
export const darkTheme = {\npalette: {\n    type: \'dark\'\n},\noverrides: {\n    MuiAppBar: {\n        colorSecondary: {\n            backgroundColor: \'#424242\', //\'#1e4c9a\',\n            color: \'#fff\'\n        },\n    },\n    MuiButton: {\n        textPrimary: {\n            color: \'#fff\',\n        }\n    },\n    MuiTypography: {\n        colorPrimary: {\n            color: \'#fff\'\n        }\n    },\n    MuiDrawer: {\n        paper: {\n            paddingTop: \'20px\'\n        }\n    },\n    MuiFilledInput: {\n        underline: {\n            \'&:after\': {\n                borderBottomColor: \'#bf9f00\'\n            }\n        }\n    },\n    MuiFormLabel: {\n        root: {\n            \'&$focused\': {\n                color: \'#bf9f00\'\n            }\n        }\n    },\n}\n}\n\nexport const lightTheme = {\n    palette: {\n        type: \'light\'\n    },\n    overrides: {\n        MuiAppBar: {\n            colorSecondary: {\n                backgroundColor: \'#2196f3\',\n                color: \'#fff\'\n            },\n        },\n    },\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
import { createMuiTheme } from \'@material-ui/core/styles\';\n\nimport { darkTheme } from \'../../layout/Theme\'\nconst Theme = createMuiTheme(darkTheme)\n\nexport default (previousState = Theme, action) => {\n  if (action.type === \'SWITCH_THEME\') {\n    return action.theme;\n  }\n  return previousState;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

另外,为了调度状态,还有一个操作(ThemeAction.js):

\n
export const switchTheme = (theme) => {\n  return {\n    type: \'SWITCH_THEME\',\n    theme\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
import Theme from \'./store/reducers/themeReducer\'\n\n\nconst App = () => {\n\n  return (\n    <div className="admin-body">\n\n      <Admin\n        customReducers={{ theme: Theme }}\n        layout={Layout}\n        authProvider={login}\n        dataProvider={dataProvider}\n        loginPage={LoginPage}\n        locale="pt"\n        customRoutes={[\n          <Route\n            key="configuration"\n            path="/configuration"\n            title="Configura\xc3\xa7\xc3\xb5es"\n            component={Configuration}\n          />\n        ]}\n      > ...\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
import React from \'react\'\nimport { useSelector, useDispatch } from \'react-redux\';\nimport Button from \'@material-ui/core/Button\';\nimport { switchTheme } from \'../../store/actions/themeAction\';\nimport { darkTheme, lightTheme } from \'../../layout/Theme\'\nimport { createMuiTheme } from \'@material-ui/core/styles\';\n\nimport Card from \'@material-ui/core/Card\';\nimport CardContent from \'@material-ui/core/CardContent\';\nimport { Title } from \'react-admin\';\n\nimport \'./styles.css\'\n\nconst Configuration = () => {\n  const dispatch = useDispatch();\n  const theme = useSelector(state => state.theme);\n\n  const mainClass = \'configPage\'\n\n  return (\n    <Card>\n      <Title title="Configura\xc3\xa7\xc3\xb5es" />\n      <CardContent className={mainClass}>\n        <div className="theme-label">\n          <p className="text" >Selecione seu Tema: </p>\n          <Button\n            variant="contained"\n            className={theme.palette.type === \'light\' ? \'active\' : \'\'}\n            onClick={() => dispatch(switchTheme(createMuiTheme(lightTheme)))}\n          >\n            Claro\n        </Button>\n          <Button\n            variant="contained"\n            className={theme.palette.type === \'dark\' ? \'active\' : \'\'}\n            onClick={() => dispatch(switchTheme(createMuiTheme(darkTheme)))}\n          >\n            Escuro\n        </Button>\n        </div>\n      </CardContent>\n    </Card>\n\n  )\n}\n\nexport default Configuration;\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

布局.js

\n
const MyLayout = (props) => {\n    const theme = useSelector(state => state.theme)\n    return (\n        <ThemeProvider theme={theme}>\n            <Layout\n                {...props}\n                appBar={AppBar}\n                sidebar={Sidebar}\n                menu={Menu}\n                notification={Notification}\n            />\n        </ThemeProvider>\n    )\n};\n
Run Code Online (Sandbox Code Playgroud)\n

应用程序.js

\n
...\n    import Layout from \'./layout\'\n    \n    const App = () => {\n    \n      return (\n        <div className="admin-body">\n    \n          <Admin\n            customReducers={{ theme: Theme }}\n            layout={Layout}\n            authProvider={login}\n            dataProvider={dataProvider}\n            loginPage={LoginPage}\n            locale="pt"\n            customRoutes={[\n              <Route\n                key="configuration"\n                path="/configuration"\n                title="Configura\xc3\xa7\xc3\xb5es"\n                component={Configuration}\n              />\n            ]}\n          >\n...\n
Run Code Online (Sandbox Code Playgroud)\n

感谢任何帮助。\n问候

\n

hen*_*dra 7

我不知道你的要求,但也许不需要将主题存储在 redux 中?您可以使用使用 React context 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);
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

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

    return (
        [...]
        <Button onClick={() => setTheme('light')} />
        [...]
    );
}
Run Code Online (Sandbox Code Playgroud)

希望这对你有用!