如何最好地在 MUI 中创建自定义样式的组件

bri*_*web 5 reactjs material-ui

看似简单的问题:

  1. 示例背景:我们有许多项目列表,并使用<List>.
  2. 示例问题:我们通常(但并非总是)希望删除注入的额外填充<List>(例如<UnpaddedList>:)。

作为另一个例子,也许我们希望大多数列表默认可折叠,并且<List onClick={...}>触发器变为<CollapsableList>。对于下面的大多数示例,未填充的内容足以说明。

初步猜测,我期望类似的内容CustomList extends List但请参阅 Facebook 继承建议)。有很多方法可以做到这一点,但是优点/缺点是什么......它们似乎是冲突的:

createStyled()

这是为了完全取代主题......在旨在打破品牌标准的时代。尽管我们可以返回并合并现有主题,但这似乎违背了该工具的目的,并且deepmerge可能会降低性能?如果它可以继承当前的主题上下文,那么这将很有效。也许很多额外的组件名称是不好的做法?例如:ListUnpaddedList UnpaddedLargeListUnpaddedLargeGreenList

请参阅https://mui.com/system/styled/#create-custom-styled-utility

createTheme()

这允许完全覆盖每个组件的样式和道具。唯一的问题是,可能不清楚为什么组件会以某种方式设置样式并保留其名称。(例如:<List>未重命名为<UnpaddedList>)如果您有一个组件从树中某处的父组件继承主题并以意外的样式显示,这可能会令人困惑。开发人员必须跟踪每个父组件才能找到主题注入的位置<ThemeProvider>。但也许这种以意想不到的方式注入子主题本身就是反模式。

此外,如果没有明确的文档/打字稿,需要进行大量阅读才能确定如何最好地传递和修改当前主题:

function extendThemeWithGreen(theme: Theme) {
  let themeOptions: ThemeOptions = { palette: { primary: { main: "green" } } };
  return createTheme(theme, themeOptions);
}

function Example() {
  return <ThemeProvider theme={extendThemeWithGreen}> example</ThemeProvider>;
}
Run Code Online (Sandbox Code Playgroud)

另一个问题是某些组件中注入了任意空白。这没有记录,也没有类型提示。发现这些的唯一方法就是寻找源代码。从那里您可能必须创建另一组样式来覆盖本机样式并向应用程序引入膨胀。

请参阅https://mui.com/system/styled/#custom-components 请参阅https://mui.com/customization/how-to-customize/#2-reusable-style-overrides

return <List {...props} />

包装组件会很好。(例如const UnpaddedList = (props) => <List disablePadding {...props} />;:)到目前为止,这已经很麻烦了。例如,道具应该是ListProps还是OverridableComponent<ListTypeMap<{}, "ul">>?或者假设最终组件和包装组件都有sx......我是否必须设置 adeepMerge来处理重复的道具。也许我只是需要花更多的时间来让它继续下去?

请参阅https://mui.com/guides/composition/#wrapping-components

sx={...}style={...}class=...

这些道具通常允许定制,但它们实际上是一次性的。例如:你<List style={{padding:0}} />也必须这样做,这会失去类型安全性(预计将来会发生问题) class可以分开,但仍然很难保留类型安全性。遗产makeStyles(是相似的

请参阅https://mui.com/system/the-sx-prop/ 请参阅https://mui.com/styles/api/#createstyles-styles-styles

<UnstyledList component={...}

这很大程度上是一个即将推出的功能,尚未完全实现。虽然这可以避免未来的 css 冲突,但它仍然不利于props覆盖(或者如果确实如此......文档中尚不清楚)

请参阅https://mui.com/customization/unstyled-components/#main-content

<Box component={List} ... />

使用Box包装器创建自定义组件。文档对此并不完全清楚。它可能是这样的:

function UnpaddedList(props: ListProps){
  return <Box component={List} disablePadding {...props} />
}
Run Code Online (Sandbox Code Playgroud)

许多组件道具都有通用修饰符......但是尚不清楚如何编写这个新组件来匹配(或扩展)原始组件道具

请参阅https://mui.com/system/box/#overriding-mui-components

variant="unpadded"

这很接近,但是它似乎会产生打字问题或导入时的特殊处理。我可以看到这会造成混乱,但如果实施不当,至少 linting 会发出警告。它没有一个好的方法来进行组合(例如variant="unpadded&collapsible":)。对于没有内置变体的组件,还不清楚如何做到这一点。

请参阅https://mui.com/customization/theme-components/#adding-new-component-variants

props

当然,有些(但不是全部)可以通过 props keep type safety 来完成。(例如<List disablePadding={true}>:)但这不是 DRY。总体来说管理起来更差(没有品牌标准)。它还使代码更难以阅读并且过于冗长

其他方法?

也许我错过了另一种方式。我也找不到任何优雅的解决方案来解决MUI 组件中的硬编码空白

Nea*_*arl 1

如果您有一个在多个位置使用的组件,并且其中只有一个需要具有一些特殊样式,或者您需要调整或修复特定布局中的边缘情况,请使用sxprop. 它适合像这样的一次性风格:

<List sx={{ p: 0 }}>
Run Code Online (Sandbox Code Playgroud)

如果您偶尔需要在组件中应用某些样式,请使用styled. 例如,如果一个组件在 100 个地方使用,但仅在 20 个地方使用,则需要禁用 padding,然后添加 padding 属性以在罕见情况下启用样式:

const options = {
  shouldForwardProp: (prop) => prop !== 'disablePadding',
};
const List = styled(
  MuiList,
  options,
)(({ theme, disablePadding = false }) => ({
  ...(disablePadding && {
    padding: 0,
  }),
}));
Run Code Online (Sandbox Code Playgroud)
// I dont always use disablePadding but sometimes I need it
<List disablePadding>
Run Code Online (Sandbox Code Playgroud)