reactjs - 在 Material ui 中将它们作为 Prop 传递时如何键入类

标签 reactjs typescript material-ui react-proptypes

我发现自己在 Material UI 中编写了一些通用组件,其中样式实际上是在父组件或祖父组件中定义的。例如:

const GenericDescendant = (props: DescendantProps) => {
  const { classesFromAncestor, ...other } = props
  return <Button className={classesFromAncestor} {...props}>Text here</Button>
}

我从 Ancestor 那里创建了一个 useStylesclasses 对象并将其向下传递:

const useDescendantStyles = makeStyles({
  selector: { 
    border: '1px solid green',
    // more customized styles
  }
})

const Ancestor = () => {
  const descendantClasses = useDescendantStyles()
  return <GenericDescendant classesFromAncestor={descendantClasses} />
}

或者另一种方式,我可以使用 withStyles 来包装在后代中使用的组件:

const GenericDescendant = (props: DescendantProps) => {
  const { tooltipClassesFromAncestor, buttonClassesFromAncestor, ...other } = props
  const CustmoizedTooltip = withStyles(tooltipClassesFromAncestor)(Tooltip);
  const CustomButton = withStyles(buttonClassesFromAncestor)(Button)
  return (
    <CustmoizedTooltip>
      <CustomButton>Text here</CustomButton>
    </CustmoizedTooltip>
  )
}

在这种情况下,我不会在 Ancestor 中调用 useStyles,我会像这样传递对象:

const Ancestor = () => {
  return <GenericDescendant classesFromAncestor={{
    selector: { 
      border: '1px solid green',
      // more customized styles
    }
  }} />
}

我不想将整个 GenericDescendant 包装在 withStyles 中,因为这样我就无法确定哪个组件正在采用从祖先。

情况略有不同,但我可以选择任何一种方式来允许自定义祖先的通用后代。但是,当将自定义样式作为 Prop 传递时,我不知道如何在 DescendantProps 中正确键入 classesFromAncestor Prop 。它实际上可以采用任何形式,并且直到运行时才知道该形式。我不想将其键入 any。定义作为 prop 传递的类类型的最佳方法是什么?

最佳答案

我一直在玩这个,有很多可能的设置。您可以通过传递单个 string 来设置 material-ui 组件的样式。 className你也可以传递 classes 的对象对应于该组件可用的不同选择器。大多数(但不是全部) Material 组件的主要选择器是 root . Button接受类(class) root , label , text , 和 many others .

当您调用 makeStyles 时你创建的是一个 object其中键是选择器,与您的输入键基本相同,值是 string生成类的类名。您可以将此类型导入为 ClassNameMap来自 @material-ui/styles .它本质上是 Record<string, string> ,但有一个可选的泛型,可以让您限制键。

export type ClassNameMap<ClassKey extends string = string> = Record<ClassKey, string>;

您传递给 makeStyles 的第一个参数或 withStyles是样式属性的键控对象。可以简化为Record<string, CSSProperties>或者您可以从 material-ui 导入复杂类型,其中包括对功能等的支持。类型 Styles<Theme, Props, ClassKey>采用三个可选泛型,其中 Theme是你的主题类型,Props是组件自己的 Prop ,ClassKey再次是受支持的选择器。 ThemeProps主要包含在内,以便您可以将样式映射为 theme 的函数或 props .

如果我们想同时为多个组件提供样式,一种简单的方法是为每个组件创建一个带有一个键的样式对象。你可以随便称呼他们。然后我们将这些样式中的每一个作为 className 传递。到组件。

const MyStyled = withStyles({
  button: {
    border: "green",
    background: "yellow"
  },
  checkbox: {
    background: "red"
  }
})(({ classes }: { classes: ClassNameMap }) => (
  <Button className={classes.button}>
    <Checkbox className={classes.checkbox} />
    Text Here
  </Button>
));

该设置允许在任何级别的嵌套中使用任意数量的组件。

我尝试制作一个可以包裹外部组件的高阶组件。您可以以此为起点并根据您的需要进行调整。

const withAncestralStyles = <Props extends { className?: string } = {}>(
  Component: ComponentType<Props>,
  style: Styles<DefaultTheme, Omit<Props, "children">, string>
) => {
  const useStyles = makeStyles(style);

  return (
    props: Omit<Props, "children"> & {
      children?: (styles: ClassNameMap) => ReactNode;
    }
  ) => {
    console.log(props);
    const classes = useStyles(props);
    const { children, className, ...rest } = props;
    return (
      <Component {...rest as Props} className={`${className} ${classes.root}`}>
        {children ? children(classes) : undefined}
      </Component>
    );
  };
};

基本上,类 classes.root将自动应用于 className parent 的所有样式都作为 Prop 传递给 child (但未应用)。我在制作 React.cloneElement 时遇到了麻烦工作,所以我要求 children组件的 function它接收 classes对象。

const StyledButton = withAncestralStyles(Button, {
  root: {
    border: "green",
    background: "yellow"
  },
  checkbox: {
    background: "red"
  }
});

const MyComponent = () => (
  <StyledButton onClick={() => alert('clicked')}>
    {(classes) => <><Checkbox className={classes.checkbox}/>Some Text</>}
  </StyledButton>
);

Code Sandbox

此方法不适用于您的 Tooltip例如因为 Tooltip是没有 root 的组件之一选择器,需要不同的样式以定位弹出窗口。

关于reactjs - 在 Material ui 中将它们作为 Prop 传递时如何键入类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65996439/

相关文章:

reactjs - 如何使用 MenuItem 进行导航? Material -ui V1

javascript - 无法从React中的子组件调用父组件函数

css - 悬停时如何更改特定元素?

javascript - onKeyDown 无响应

typescript - Vim 不读取 TypeScript 文件

Angular : how to trigger bindings manually?

javascript - TableRow Material-UI 之间的间距

javascript - 从 firebase 获取数据后刷新选择器项目

html - Svg 内部未与中心对齐

typescript - 类星体/电容器错误: ".../node_modules/http-proxy-middleware/dist/index has no exported member ' Config'"