如何重构共享主题信息的material-ui demo

How to refactor material-ui demo that shares theme information

我正在查看将以下示例代码用于小型应用程序的框架:

https://material-ui.com/components/drawers/#MiniDrawer.js

在尝试将元素重构为单独的组件时,我遇到了一个问题。将代码剪切并粘贴到全新的 create-react-app 应用程序中效果很好。然而,一旦我尝试将 AppBar 和 Drawer 重构为单独的组件,我怀疑它们会失去对彼此的跟踪。

My version codesandbox.io。

我首先将 useStyles 提取到一个单独的文件中:

// useStyles.js
import { makeStyles } from '@material-ui/core/styles';

const drawerWidth = 240;

const useStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
  },
  appBar: {
    zIndex: theme.zIndex.drawer + 100,
    transition: theme.transitions.create(['width', 'margin'], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen,
    }),
  },
  appBarShift: {
    marginLeft: drawerWidth,
    width: `calc(100% - ${drawerWidth}px)`,
    transition: theme.transitions.create(['width', 'margin'], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.enteringScreen,
    }),
  },
  menuButton: {
    marginRight: 36,
  },
  hide: {
    display: 'none',
  },
  drawer: {
    width: drawerWidth,
    flexShrink: 0,
    whiteSpace: 'nowrap',
  },
  drawerOpen: {
    width: drawerWidth,
    transition: theme.transitions.create('width', {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.enteringScreen,
    }),
  },
  drawerClose: {
    transition: theme.transitions.create('width', {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen,
    }),
    overflowX: 'hidden',
    width: theme.spacing(7) + 1,
    [theme.breakpoints.up('sm')]: {
      width: theme.spacing(9) + 1,
    },
  },
  toolbar: {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'flex-end',
    padding: theme.spacing(0, 1),
    ...theme.mixins.toolbar,
  },
  content: {
    flexGrow: 1,
    padding: theme.spacing(3),
  },
}));

export default useStyles;

...并将 AppBar 和 Drawers 放入自己的文件中:

// MyUIAppBar.js
import React from 'react';
import clsx from 'clsx';
// ...

import { Mail, Notifications, Menu } from '@material-ui/icons';

function MyUIAppBar(props) {

  const classes = props.classes;
  const toggleDrawer = props.toggleDrawer;
  const open = props.open;

  return(
    <AppBar
    position="fixed"
    className={clsx(classes.appBar, {
      [classes.appBarShift]: open,
    })}
  >
    <Toolbar>
      <IconButton
        color="inherit"
        aria-label="open drawer"
        onClick={toggleDrawer}
        edge="start"
        className={clsx(classes.menuButton, {
          [classes.hide]: open,
        })}
      >
        <Menu />
      </IconButton>
      <Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}>
        MDS MyUI
      </Typography>

      // ...

    </Toolbar>
  </AppBar>
);
}

export default MyUIAppBar;

// MyUIDrawer.js
import React from 'react';
import clsx from 'clsx';
import Drawer from '@material-ui/core/Drawer';
import IconButton from '@material-ui/core/IconButton';
import { ChevronLeft, ChevronRight } from '@material-ui/icons';

function MyUIDrawer(props) {

  const classes = props.classes;
  const toggleDrawer = props.toggleDrawer;
  const open = props.open;

  return (
    <Drawer
      variant="permanent"
      className={clsx(classes.drawer, {
        [classes.drawerOpen]: open,
        [classes.drawerClose]: !open,
      })}
      classes={{
        paper: clsx({
          [classes.drawerOpen]: open,
          [classes.drawerClose]: !open,
        }),
      }}
      open={open}
      onToggle={toggleDrawer}
    >
      <div className={classes.toolbar}>
        <IconButton onClick={toggleDrawer}>
          {open ? <ChevronRight /> : <ChevronLeft />}
        </IconButton>
      </div>
      {props.children}
    </Drawer>
  );
}

export default MyUIDrawer;

并且在主 App 文件中,我必须将主题、打开、类 和 toggleDrawer 作为属性传递给两个组件:

// App.js
import React from 'react';
import useStyles from './styles';
import WebUIDrawer from './components/WebUIDrawer';
import WebUIAppBar from './components/WebUIAppBar';
import { useToggle } from './hooks';
import { useTheme } from '@material-ui/core/styles';
// ... various imports here

export default function App() {
  const classes = useStyles();
  const theme = useTheme();
  const [ open, toggleDrawer ] = useToggle(true);

  return (
    <div className={classes.root}>
      <CssBaseline />
      <WebUIAppBar theme={theme} open={open} classes={classes} toggleDrawer={toggleDrawer}/>
      <WebUIDrawer open={open} classes={classes} toggleDrawer={toggleDrawer} >
        <List>
          {['Eeny', 'Meeny', 'Miney', 'Mo'].map((text, index) => (
            <ListItem button key={text}>
              <ListItemIcon>{index % 2 === 0 ? <MoveToInbox /> : <Mail />}</ListItemIcon>
              <ListItemText primary={text} />
            </ListItem>
          ))}
        </List>
      </WebUIDrawer>
      <Main classes={classes}/>
    </div>
  );
}

结果有点效果,但菜单的分层和偏移似乎有问题。我怀疑主题在某种程度上没有在两者之间更新。有没有更好的方法在组件之间共享样式信息?是否可以使用 React 上下文来执行此操作?坦率地说,我很难过。 :-(

主要问题是您导入 useStyles 的位置。它需要放在最后,以确保它定义的样式位于 之后 <head> 中的默认样式。有关原因的详细信息,请参阅此相关答案:.

这是您的沙盒的固定版本:https://codesandbox.io/s/fixed-usestyles-import-q0m8z

我所做的只是将 useStyles 导入移动到 App.js