Я пытаюсь обновить состояние темы приложения, которое я создаю, в пользовательском интерфейсе с использованием реаги. Я пытаюсь создать переключатель, который переключается между светлым и темным режимом. Мой компонент приложения является компонентом на основе классов, поэтому я использую оператор материала UI useTheme()
.
Пока мой код выглядит так: Компонент приложения, в котором создается тема:
import React from 'react';
import { Router, Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { history } from '../_helpers';
import { alertActions } from '../_actions';
import { PrivateRoute } from '../_components';
import { HomePage } from '../HomePage';
import { IntroPage } from '../IntroPage';
import { LoginPage } from '../LoginPage';
import { RegisterPage } from '../RegisterPage';
import { SurveyPage, SurveryPage } from '../SurveyPage';
import { SideBar } from '../SideBar';
import { NewDiagnosisSplash } from '../NewDiagnosisSplashPage';
import { RecoveryPlanDashboard } from '../RecoveryPlanDashboard';
import { TodayExercises } from '../TodayExercises';
import { withTheme } from '@material-ui/core/styles';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import { blue, grey, white } from '@material-ui/core/colors';
import '../css/App.css';
import { Grid, Paper } from '@material-ui/core';
class App extends React.Component {
constructor(props) {
super(props);
history.listen((location, action) => {
// clear alert on location change
this.props.clearAlerts();
});
this.handleCheckBox = this.handleCheckBox.bind(this)
this.theme = this.theme.bind(this)
this.state = {
checked: true,
theme: null
}
}
theme(){
this.setState({
theme: createMuiTheme({
palette: {
type: this.state.checked ? "dark": "light",
primary: grey,
secondary: blue,
}, typography: {
h3: {
fontWeight: 100,
color: blue
}
}
})
})}
componentWillMount(){
this.theme()
}
componentDidMount(){
console.log(this.state.theme)
}
handleCheckBox(e) {
var oldTheme = {...this.state.theme}
oldTheme.flag = true;
this.setState({theme: createMuiTheme({
palette: {
type: this.state.checked ? "dark": "light",
primary: grey,
secondary: blue,
}, typography: {
h3: {
fontWeight: 100,
color: blue
}
}
})})
this.setState({
checked: !this.state.checked
})
console.log(this.state.checked)
}
render() {
const { alert } = this.props;
return (
<ThemeProvider theme={this.state.theme}>
<Paper style={{height: "100vh"}}>
<Grid container direction="column" justify="center"
alignItems="center" >
{alert.message &&
<div className={`alert ${alert.type}`}>{alert.message}</div>
}
<Router history={history}>
<SideBar checked={this.state.checked} changed={this.handleCheckBox}>
</SideBar>
<Grid item xs={false} sm={4} />
<Grid item xs={12} sm={4}>
<Switch>
<PrivateRoute exact path="/" component={HomePage} />
<Route path="/intro" component={IntroPage} />
<Route path="/login" component={LoginPage} />
<Route path="/register" component={RegisterPage} />
<Route path="/survey" component={SurveryPage} />
<Route path="/new-diagnosis" component={NewDiagnosisSplash} />
<Route path="/recovery-plans" component={RecoveryPlanDashboard} />
<Route path="/today-exercises" component={TodayExercises} />
<Redirect from="*" to="/" />
</Switch>
</Grid>
<Grid item xs={false} sm={4} />
</Router>
</Grid>
</Paper>
</ThemeProvider>
);
}
}
function mapState(state) {
const { alert } = state;
return { alert };
}
const actionCreators = {
clearAlerts: alertActions.clear
};
const connectedApp = connect(mapState, actionCreators)(App);
const withThemesObj = withTheme(connectedApp)
export { withThemesObj as App };
И это боковая панель с кнопкой:
import { connect } from 'react-redux';
import React, { useDebugValue, useEffect } from 'react';
import clsx from 'clsx';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import CssBaseline from '@material-ui/core/CssBaseline';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import { Link } from 'react-router-dom';
import { userActions } from '../_actions';
import PreviousDiagnosis from '../assets/imgs/PreviousDiagnosis.svg'
import Account from '../assets/imgs/Account.svg'
import StartDiagnosis from '../assets/imgs/StartDiagnosis.svg'
import FitnessIcon from '../assets/imgs/Fitness.svg'
import SettingsIcon from '../assets/imgs/Settings.svg'
import AboutIcon from '../assets/imgs/About.svg'
import LegalIcon from '../assets/imgs/Legal.svg'
import SignOutIcon from '../assets/imgs/SignOut.svg'
import Switch from '@material-ui/core/Switch';
console.log(userActions.getAll)
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
appBar: {
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginRight: drawerWidth,
},
title: {
flexGrow: 1,
color: theme.palette.text.primary
},
hide: {
display: 'none',
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: 'flex-start',
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginRight: -drawerWidth,
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginRight: 0,
},
}));
function PersistentDrawerRight(props) {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const { checked, changed } = props;
useEffect(()=>{
//props.getAll()
})
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const helloStyle = {
fontWeight: 200
}
const loginText = () => {
if(props.user === undefined){
return "Welcome to Sina Tech"
} if (props.user.firstName){
return props.user.firstName
}
}
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar>
<Typography variant="h5" noWrap className={classes.title} color="primary">
<Link color="primary" underline="none" variant="inherit" to={'/'}><span style={helloStyle}>Hello,</span> {loginText()}</Link>
</Typography>
<IconButton
color="inherit"
aria-label="open drawer"
edge="end"
onClick={handleDrawerOpen}
className={clsx(open && classes.hide)}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.drawerHeader} />
</main>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="right"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</div>
<Divider />
<List>
<Link to={'/survey'}>
<ListItem button key={"New Diagnosis"}>
<ListItemIcon><StartDiagnosis /></ListItemIcon>
<ListItemText>Start Diagnosis</ListItemText>
</ListItem>
</Link>
<Link to={'/survey'}>
<ListItem button key={"Continue Diagnosis"}>
<ListItemIcon><Account /></ListItemIcon>
<ListItemText>Account</ListItemText>
</ListItem>
</Link>
<Link to={'/survey'}>
<ListItem button key={"Previous Diagnosis"}>
<ListItemIcon><PreviousDiagnosis /></ListItemIcon>
<ListItemText>Previous Diagnosis</ListItemText>
</ListItem>
</Link>
<Link to={'/recovery-plans'}>
<ListItem button key={"Recovery Plans"}>
<ListItemIcon><FitnessIcon /></ListItemIcon>
<ListItemText>Recovery Plans</ListItemText>
</ListItem>
</Link>
<Link to={'/survey'}>
<ListItem button key={"Settings"}>
<ListItemIcon><SettingsIcon /></ListItemIcon>
<ListItemText>Settings</ListItemText>
</ListItem>
</Link>
<Link to={'/survey'}>
<ListItem button key={"About Us"}>
<ListItemIcon><AboutIcon /></ListItemIcon>
<ListItemText>About Us</ListItemText>
</ListItem>
</Link>
<Link to={'/survey'}>
<ListItem button key={"Legal"}>
<ListItemIcon><LegalIcon /></ListItemIcon>
<ListItemText>Legal</ListItemText>
</ListItem>
</Link>
<Link to="/login">
<ListItem button key={"Sign Out"}>
<ListItemIcon><SignOutIcon /></ListItemIcon>
Sign Out
</ListItem>
</Link>
<Switch
checked={props.checked}
onChange={props.changed}
name="checkedA"
inputProps={{ 'aria-label': 'secondary checkbox' }}
/>
</List>
</Drawer>
</div>
);
}
function mapState(state) {
const { users, authentication } = state;
const { user } = authentication;
return { user, users };
}
const actionCreators = {
getUsers: userActions.getAll,
deleteUser: userActions.delete
}
const connectedSideBar = connect(mapState, actionCreators)(PersistentDrawerRight);
export { connectedSideBar as SideBar };
Моя проблема заключается в том, что мне нужно повторно инициализировать состояние здесь:
constructor(props) {
super(props);
history.listen((location, action) => {
// clear alert on location change
this.props.clearAlerts();
});
this.handleCheckBox = this.handleCheckBox.bind(this)
this.theme = this.theme.bind(this)
this.state = {
checked: true,
theme: null
}
}
theme(){
this.setState({
theme: createMuiTheme({
palette: {
type: this.state.checked ? "dark": "light",
primary: grey,
secondary: blue,
}, typography: {
h3: {
fontWeight: 100,
color: blue
}
}
})
})}
componentWillMount(){
this.theme()
}
componentDidMount(){
console.log(this.state.theme)
}
handleCheckBox(e) {
var oldTheme = {...this.state.theme}
oldTheme.flag = true;
this.setState({theme: createMuiTheme({
palette: {
type: this.state.checked ? "dark": "light",
primary: grey,
secondary: blue,
}, typography: {
h3: {
fontWeight: 100,
color: blue
}
}
})})
this.setState({
checked: !this.state.checked
})
console.log(this.state.checked)
}
функциональность работает, как и ожидалось, но это очень грязный код, требующий повторной инициализации состояния, есть идеи, как я могу это исправить?