react-admin version: 3.8.4
I have a react-admin app and I'm trying to switch between light and dark themes.
Below you can see the Theme.js where I export the two objects with default theme overrides as described on docs. (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'
},
},
},
}
I did also a customReducer as you can see below, called 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;
}
Also to dispatch the state there is an action (ThemeAction.js):
export const switchTheme = (theme) => {
return {
type: 'SWITCH_THEME',
theme
}
}
To set the custom Reducer, as explained on the docs, I set on <Admin>
the attribute custom Reducer as below:
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}
/>
]}
> ...
To switch between the themes and set into the store I'm using this configuration page:
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;
All this structure is working fine, I mean the global state theme is updating correctly, so the reducer is getting the clicked theme and switching the state.
The issue is that, as described on docs, to change the default theme we have to pass the new theme as an attribute on the tag <Admin>
like: <Admin theme={theme}> </admin>
But the tag <Admin>
is set into the App.js, and the redux context isn't above the App, so isn't possible to get the global theme state into it.
Then my question is how can I use the global theme state that I've created to pass as an Application theme.
I already tried passing as Layout's parent as you can see below, but the theme not reflected on the application.
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}
/>
]}
>
...
Appreciate any help. Regards
I don't know your requirements, but maybe it's not necessary to store the theme in redux? You could use a solution using the react context api, like so:
And use it like that:
Hope that works for you!