【问题标题】:How to use custom props and theme with material-ui styled components API (Typescript)?如何将自定义道具和主题与 Material-ui 样式的组件 API(Typescript)一起使用?
【发布时间】:2021-01-20 14:04:53
【问题描述】:

我正在尝试使用 Material UI 样式的组件 API 来注入自定义主题和特定自定义元素的一些道具。

我有一个自定义主题或一些道具可以工作,但不能同时使用

自定义主题

我的自定义主题在单独的文件中定义如下...

export interface ITheme extends Theme {
    sidebar: SideBarTheme;
}

type SideBarTheme = {
    // Various properties
};

const theme = createMuiTheme(
    {
        // Normal theme modifications
    },
    {
        sidebar: {
            // Custom theme modifications
        },
    } as ITheme
);

然后我将其与 Material-UI 样式的组件 API 一起使用,如下所示......

const Profile = styled(Button)(({ theme }: { theme: ITheme }) => ({
    backgroundColor: theme.sidebar.profile.backgroundColor,
    paddingTop: 20,
    paddingBottom: 20
}));

这在我的 jsx 中使用 <Profile /> 可以正常工作。

道具

对于道具,我基本上使用的是示例here的精确副本

interface MyButtonProps {
    color: string;
}

const Profile = styled( ({ color, ...other }: MyButtonProps & Omit<ButtonProps, keyof MyButtonProps>) => (
    <Button {...other} />
  ))({
    backgroundColor: (props: MyButtonProps) => props.color,
    paddingTop: 20,
    paddingBottom: 20
});

当使用&lt;Profile color="red"/&gt;时,这在我的 jsx 中运行良好

如何让这两者与 typescript 一起工作?

我确实尝试像下面这样组合它们,但道具没有传递...

const Profile = styled( ({ color, ...other }: MyButtonProps & Omit<ButtonProps, keyof MyButtonProps>) => (
    <Button {...other} />
  ))(({ theme }: { theme: ITheme }) => ({
    backgroundColor: (props: MyButtonProps) => props.color,
    color: theme.sidebar.profile.backgroundColor,
    paddingTop: 20,
    paddingBottom: 20
}));

我尝试的任何其他方法都会让 TypeScript 感到不安。 material-ui styled 函数也太复杂了,我看不懂。

非常感谢,

【问题讨论】:

    标签: reactjs typescript material-ui


    【解决方案1】:

    这个问题也几乎把我逼疯了,但是我设法用这种方法让它工作:

    import React from 'react'
    import { styled, Theme, withTheme } from '@material-ui/core/styles'
    import { Box, BoxProps } from '@material-ui/core'
    
    interface SidebarSectionProps {
      padded?: boolean
      theme: Theme
    }
    
    export const SidebarSectionComponent = styled(
      (props: SidebarSectionProps & BoxProps) => <Box {...props} />
    )({
      marginBottom: ({ theme }: SidebarSectionProps) => theme.spacing(3),
      paddingLeft: ({ theme, padded }: SidebarSectionProps) =>
        padded ? theme.spacing(3) : 0,
      paddingRight: ({ theme, padded }: SidebarSectionProps) =>
        padded ? theme.spacing(3) : 0
    })
    
    export const SidebarSection = withTheme(SidebarSectionComponent)
    

    【讨论】:

      【解决方案2】:

      我不确定使用 styled-components 或材质 styled HOC 是否有意义,因为它们更适合设置不依赖于 Props 的固定样式。

      但是您可以通过Button 组件的style 属性设置您想要的样式。

      这是一个似乎可行的解决方案:

      interface AddedProps {
        theme: Theme;
        color: string;
      };
      
      type InnerProps = AddedProps & Omit<ButtonProps, keyof AddedProps>
      
      const InnerProfile = ({color, theme, ...other}: InnerProps) => (
        <Button
          {...other}
          style={{
            ...other.style,
            paddingTop: 20,
            paddingBottom: 20,
            backgroundColor: color,
            color: (theme as MyTheme).sidebar?.profile?.backgroundColor,
          }}
        />
      )
      
      export const Profile = withTheme( InnerProfile );
      

      我们说我们的Profile 需要两个AddedProps:颜色和主题。我们写InnerProfile 假设这些道具存在。然后在一个单独的步骤中,我们应用 withTheme HOC 以便设置 theme 属性。 color 属性是必需的,并且必须由您在使用 &lt;Profile /&gt; 时设置。

      现在来看看这个古怪的疯狂:

      color: (theme as MyTheme).sidebar?.profile?.backgroundColor
      

      发生的事情是withTheme 知道它注入了一个主题,但它不知道它注入了您的特定主题,所以打字稿不知道主题有一个属性sidebar,因为这是您添加的自定义属性。您需要通过(theme as MyTheme) 告诉打字稿“假设此主题具有 MyTheme 的属性”。您可以通过在创建自定义主题的对象上使用 typeof 来获取该类型 MyTheme

      由于我无权访问您的自定义主题对象,我所做的是定义一个具有所需属性路径的接口,但所有内容都是可选的。看起来像:

      interface MyTheme extends Theme {
        sidebar?: {
          profile?: {
            backgroundColor?: string;
          } 
        }
      }
      

      并且我在访问sidebar?.profile?.backgroundColor 时使用了打字稿?. 表示法以避免“对象可能未定义”错误。

      【讨论】:

      • 谢谢,但对于我的用例来说,它看起来有点过于冗长。我想我已经找到了一种方法来实现我所追求的,方法是将我的自定义元素拆分为它自己的组件,然后将道具和主题传递给该组件内的makeStyles。似乎比尝试同时使用styled 更简单。正如您所建议的那样,styled 通常更适合自定义固定样式。
      猜你喜欢
      • 2021-09-11
      • 2019-09-01
      • 1970-01-01
      • 2020-06-30
      • 2020-11-23
      • 1970-01-01
      • 2021-11-14
      • 2019-07-15
      • 2020-04-09
      相关资源
      最近更新 更多