如何将自定义道具和主题与material-ui风格的组件API(Typescript)一起使用?

Kon*_*262 7 typescript reactjs material-ui

我正在尝试使用 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
);
Run Code Online (Sandbox Code Playgroud)

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

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

这在我的 jsx 中工作得很好<Profile />

道具

对于道具,我基本上使用了此处示例的精确副本

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
});
Run Code Online (Sandbox Code Playgroud)

使用时这在我的 jsx 中工作得很好<Profile color="red"/>

我如何让这两者与打字稿一起工作?

我确实尝试将它们组合起来,如下所示,但道具没有传递下去......

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
}));
Run Code Online (Sandbox Code Playgroud)

我尝试的任何其他操作都会让 TypeScript 感到不安。Material-uistyled函数也太复杂了,我无法理解。

非常感谢,

小智 9

这个问题也几乎让我发疯,但是我已经设法用这种方法使它工作:

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)
Run Code Online (Sandbox Code Playgroud)


Lin*_*ste 0

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

style但是你可以通过组件的 prop来设置你想要的样式Button

这是一个似乎有效的解决方案:

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 );
Run Code Online (Sandbox Code Playgroud)

我们说我们Profile需要两个AddedProps:颜色和主题。我们InnerProfile假设这些道具存在。然后在一个单独的步骤中,我们应用withThemeHOC 以便theme设置 prop。该color道具是必需的,并且必须由您在使用时设置<Profile />

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

color: (theme as MyTheme).sidebar?.profile?.backgroundColor
Run Code Online (Sandbox Code Playgroud)

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

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

interface MyTheme extends Theme {
  sidebar?: {
    profile?: {
      backgroundColor?: string;
    } 
  }
}
Run Code Online (Sandbox Code Playgroud)

?.在访问时使用了打字稿符号sidebar?.profile?.backgroundColor以避免“对象可能未定义”错误。