【问题标题】:Box vs className vs style for vertical spacing in Material UIMaterial UI中垂直间距的Box vs className vs style
【发布时间】:2020-10-15 03:10:09
【问题描述】:

当我看到一个使用 Material UI 实现的设计时,部分标题、表单标签、输入字段等之间总是有一些垂直空间。似乎有几种方法可以实现这一点:

  1. 将每个<Typography /><Checkbox /> 等包裹在<Box paddingBottom={2} /> 中。
  2. 为每个带有间距的元素创建一个类,例如
const useStyles = makeStyles(theme => ({ subHeader: { marginBottom: theme.spacing(2) } }));
...
const classes = useStyles();
...
<Typography className={classes.subHeader} />
  1. 使用内联样式,例如
const theme = useTheme();
<Typography style={{ marginBottom: theme.spacing(2) }} />

这些方法在我看来都不合适。

第一个在你的 HTML 代码中添加了很多额外的divs,并保证相邻的概念元素永远不会相邻;它们总是嵌套的。

<div class="MuiBox-root">
  <span class="MuiTypography-root" />
</div>
<div class="MuiBox-root">
  <span class="MuiTypography-root" />
</div>

使用第二个,您最终会创建许多相当无意义的类,以适应每个元素下方需要不同的间距,纯粹出于设计/美学原因,而不是语义原因,例如带有marginBottom: 2 的类和带有marginBottom: 3 的类。

第三个选项似乎很有意义,因为将间距逻辑提取到可重用代码中似乎有点过头了,但内联样式通常不受欢迎,并且必须在每个组件中调用 const theme = useTheme() 似乎并不正确。

TLDR; 在 Material UI 中垂直间隔组件的推荐方式是什么?

【问题讨论】:

  • 您不满意纯粹出于审美原因创建类,也不满意使用内联样式,但实际上只有这两种方法可以做到。无需担心出于美学原因创建类,您将永远无法从语义上证明每一个设计和间距细节的合理性。
  • 也就是说,想要避免创建一堆都做marginBottom: 2 的类是完全合理的。为此,您可以在组件之间共享 makeStyles 钩子

标签: reactjs material-ui


【解决方案1】:

我建议使用Boxclone 属性。这会导致它将样式添加到其子元素(通过React.cloneElement),而不是用额外的元素包装它。

下面的示例为第一个排版添加了底部边距,为第二个排版添加了左边距,而没有在 html 中引入任何额外的包装元素。

import React from "react";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";

export default function App() {
  return (
    <>
      <Box mb={3} clone>
        <Typography variant="h5" color="primary">
          Some Text
        </Typography>
      </Box>
      <Box ml={2} clone>
        <Typography color="primary">Later Text</Typography>
      </Box>
    </>
  );
}

不幸的是,正如 cmets 中所讨论的,当 Box 设置的样式与包装组件设置的样式(例如 @ 987654333@) 从那时起,导入顺序会影响获胜者(不仅是您关注的特定文件中的导入顺序,还包括它们在应用程序中的首次导入顺序)。

针对这些情况的一个解决方案是创建您自己的包装器组件来模仿您想要经常使用的Box 中的功能。例如,下面是一个可以用来代替Typography 以类似Box 的方式控制边距的组件:

import * as React from "react";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";
import clsx from "clsx";

const useStyles = makeStyles(theme => ({
  margin: ({ mb, mt, ml, mr }) => ({
    marginBottom: mb === undefined ? undefined : theme.spacing(mb),
    marginTop: mt === undefined ? undefined : theme.spacing(mt),
    marginLeft: ml === undefined ? undefined : theme.spacing(ml),
    marginRight: mr === undefined ? undefined : theme.spacing(mr)
  })
}));

const TypographyWithMargin = React.forwardRef(function TypographyWithMargin(
  { className, mb, ml, mt, mr, ...other },
  ref
) {
  const classes = useStyles({ mb, ml, mt, mr });
  return (
    <Typography
      {...other}
      className={clsx(className, classes.margin)}
      ref={ref}
    />
  );
});
export default TypographyWithMargin;

然后可以这样使用:

import React from "react";
import Typography from "./TypographyWithMargin";

export default function App() {
  return (
    <>
      <Typography mb={3} variant="h5" color="primary">
        Some Text
      </Typography>
      <Typography ml={2} color="primary">
        Later Text
      </Typography>
    </>
  );
}

【讨论】:

  • 我不知道clone。看起来像一个干净的选择。我会在接受之前再给它一些时间,看看是否有更多答案。
  • 由于 .MuiTypography-root 设置 margin-bottom: 0; 并优先考虑,我在实施此答案时遇到问题。我怀疑这是由于此处解释的导入顺序:github.com/mui-org/material-ui/issues/15993。订购进口商品的推荐解决方案对我不起作用。我怀疑 Next.js 正在改变顺序。
  • 我怀疑 next.js 正在改变顺序,但你必须注意整个应用程序中的顺序,而不仅仅是一个文件。如果第一次导入 Box 在第一次导入 Typography 之前,那么 Typography 将获胜。
  • 对于 v5(至少还有 6 个月),Box 的功能可能会融入核心组件中,然后您将拥有更理想的选择。
  • 感谢@Ryan,我非常感谢后续解决方案。期待出炉的解决方案。
【解决方案2】:

我已经申请了 this,作者是 Heydon Pickering。

我创建了一个组件Vertical.js

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Box } from '@material-ui/core';

const useStyles = makeStyles((theme) => ({
  vertical: {
    '& > *+*': {
      marginTop: '1.5rem',
    },
  },
}));

const Vertical = ({ children }) => {
  const classes = useStyles();
  return <Box className={classes.vertical}>{children}</Box>;
};

export default Vertical;

然后在任何其他组件中使用它,例如Example.js:

import React from 'react';
import Vertical from './Vertical';

const Example = () => {
  return (
    <Vertical>
      <Component/>
      <Component />
      <Another />
      <AnotherComponent />
    </Vertical>
  );
};

export default Example;

【讨论】:

    【解决方案3】:

    您可以通过在组件之间与一些边距实用程序类共享 makeStyles 挂钩来避免创建一堆重复的 CSS 类:

    // useMargins.js
    
    import { makeStyles } from "@material-ui/core/styles"
    
    export const useMargins = makeStyles((theme) => ({
      mt2: { marginTop: theme.spacing(2) },
      ml2: { marginLeft: theme.spacing(2) },
      mr2: { marginRight: theme.spacing(2) },
      mb2: { marginBottom: theme.spacing(2) },
    }))
    
    // MyComponent.js
    
    import * as React from "react"
    import Typography from "@material-ui/core/Typography"
    import { useMargins } from "./useMargins"
    
    export default function MyComponent(props): React.Node {
      const { mb2 } = useMargins()
      return <Typography className={mb2}>Hello</Typography>
    }
    

    【讨论】:

      猜你喜欢
      • 2019-10-17
      • 1970-01-01
      • 2017-10-10
      • 2011-02-14
      • 1970-01-01
      • 2021-04-05
      • 2019-08-08
      • 1970-01-01
      • 2011-03-29
      相关资源
      最近更新 更多