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

Posted

技术标签:

【中文标题】如何重构分享主题信息的material-ui demo【英文标题】:How to refactor material-ui demo that shares theme information 【发布时间】:2020-02-11 05:39:32 【问题描述】:

我正在查看使用以下示例代码围绕一个小应用程序的框架:

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% - $drawerWidthpx)`,
    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 上下文来执行此操作?坦率地说,我很难过。 :-(

【问题讨论】:

如果您可以包含一个重现您的问题的CodeSandbox,将会很有帮助。 你的愿望就是我的命令 ;-) 希望这能让问题更清楚...codesandbox.io/s/thirsty-mestorf-y46x7 【参考方案1】:

主要问题是useStyles 的导入位置。它需要放在最后,以确保将其定义的样式置于&lt;head&gt; 中的默认样式之后。有关原因的详细信息,请参阅此相关答案:Material UI v4 makeStyles exported from a single file doesn't retain the styles on refresh。

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

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

【讨论】:

是的,...但我怀疑您使用的是工程师的锤子... 10 秒敲击它,2 年知道敲击它的位置。谢谢瑞恩,你是一个理智的保护者:-)

以上是关于如何重构分享主题信息的material-ui demo的主要内容,如果未能解决你的问题,请参考以下文章

如何在故事书中显示 material-ui 主题对象

如何在 material-ui 主题中导入和使用自定义字体?

更改自定义主题 Material-UI

如何更改 Material-UI AppBar 的明暗主题颜色?

如何在不使用主题的情况下自定义 Material-UI 的下划线?

活动CSDN技术主题月:深度学习框架的重构与思考