将 props 传递给 Material UI 样式

114
给定的Card代码如这里所示。我该如何更新卡片样式或任何Material UI样式为:
const styles = theme => ({
  card: {
  minWidth: 275,
},

如下所示:

const styles = theme => ({
  card: {
  minWidth: 275, backgroundColor: props.color
},

当我尝试了最新的版本时,我得到了以下结果
Line 15:  'props' is not defined  no-undef

当我更新代码为:

const styles = theme =>  (props) => ({
  card: {
  minWidth: 275, backgroundColor: props.color
},

同样

 const styles  = (theme ,props) => ({
   card: {
   minWidth: 275, backgroundColor: props.color
 },

不是

const styles = theme => ({
  card: {
  minWidth: 275, backgroundColor: props.color
},

我在网页上得到的组件卡片样式很混乱。

顺便说一下,我是这样传递 props 的:

<SimpleCard backgroundColor="#f5f2ff" />

请帮忙!

4
我不明白为什么我被踩了,而他/她却没有解释原因! - Diamond
2
我认为你需要发布组件代码。这样不太清楚吗? - pritesh
2
@pritesh 那将是不必要的代码,因为我已经展示了需要的代码。此外,我在Material UI网站上提供了原始卡片代码的参考。所以我应该显示整个代码吗?请记住,我不是在谈论一个错误,我是在问如何完成某件事情。如果我谈论一个错误,那么我必须展示我编写的代码。但是对于如何做某事,我已经展示了我使用代码的尝试。你现在有什么意见? - Diamond
由于您正在样式函数内部访问props,因此如果样式在React组件内定义,则只有props可用。第15行:'props'未定义no-undef。您可能会因此而出现此错误。因此,编写样式的位置以及如何访问它很重要。因此,我不确定您的代码实际上是如何工作的。最好只显示组件代码? - pritesh
谢谢@Matt。但是我该如何实现我的目的呢?我将在地图迭代器中包含卡片,并且会生成许多卡片,我想通过为它们发送不同的颜色来使它们独特。 - Diamond
显示剩余4条评论
16个回答

161

删除了旧回答,因为它没有存在的理由。

这是您想要的:

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

const useStyles = makeStyles({
    firstStyle: {
        backgroundColor: props => props.backgroundColor,
    },
    secondStyle: {
        color: props => props.color,
    },
});

const MyComponent = ({children, ...props}) =>{
    const { firstStyle, secondStyle } = useStyles(props);
    return(
        <div className={`${firstStyle} ${secondStyle}`}>
            {children}
        </div>
    )
}

export default MyComponent;

现在你可以这样使用它:

<MyComponent color="yellow" backgroundColor="purple">
    Well done
</MyComponent>

官方文档


使用函数组件和钩子(hooks),是否可以实现相同的效果(在每次渲染时不创建新的样式组件)?我对React hooks很新,所以只是询问。 - Łukasz Jagodziński
是的,你可以这样做。你可以使用 useEffect 钩子,并将 style 属性传递给它,以确保它仅在 style 属性更改时重新渲染。 - serdar.sanri
这个答案不仅帮助我解决了传递props到这些样式的问题,还帮助我更好地理解了makeStyles方法。谢谢! - Luis Febro
1
如果我将useStyle声明在一个单独的文件中怎么办?我尝试过使用const styles = (props) => makeStyles({});,但没有成功。 - nautilor
1
makeStyles现在已经被弃用。 - danielrvt
显示剩余2条评论

69

如何在Material UI中同时使用props和theme的解决方案:

const useStyles = props => makeStyles( theme => ({
    div: {
        width: theme.spacing(props.units || 0)  
    }
}));

export default function ComponentExample({ children, ...props }){
    const { div } = useStyles(props)();
    return (
        <div className={div}>{children}</div>
    );
}

这是用于“道具”和“主题”的资金。 - GoClimbColorado
1
喜欢这个答案! - Erik Rybalkin
1
如果useStyles在单独的文件中,似乎无法正常工作。有什么解决方案吗? - Srinjoy Santra
4
你确定在样式声明后面添加了另一个 () 吗?const { div } = useStyles(props)(); - rychrist88

62

这里是 Typescript 的解决方案:

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

export interface StyleProps {
    height: number;
}

const useStyles = makeStyles<Theme, StyleProps>(theme => ({
  root: {
    background: 'green',
    height: ({height}) => height,
  },
}));

export default function Hook() {

  const props = {
    height: 48
  }

  const classes = useStyles(props);
  return <Button className={classes.root}>Styled with Hook API</Button>;
}

如果你想试玩一下,可以在这个 CodeSandbox里尝试一下。


基于 TypeScript 的 React 实现的简洁优雅解决方案 - Abhishek Gurjar

55

MUI v5中,使用styled()创建样式对象时,可以通过以下方式访问props:

import { styled } from "@mui/material";

const StyledBox = styled(Box)(({ theme, myColor }) => ({
  backgroundColor: myColor,
  width: 30,
  height: 30
}));

对于使用 TypeScript 的用户,您还需要将属性类型添加到 CreateStyledComponent 中:

type DivProps = {
  myColor: string;
};

const Div = styled(Box)<DivProps>(({ theme, myColor }) => ({
  backgroundColor: myColor,
  width: 30,
  height: 30
}));

<StyledBox myColor="pink" />

如果您想在自定义组件(如BoxTypography)中使用系统属性,可以像下面的示例一样使用extendSxProp
import { unstable_extendSxProp as extendSxProp } from "@mui/system";

const StyledDiv = styled("div")({});

function DivWithSystemProps(inProps) {
  const { sx } = extendSxProp(inProps);
  return <StyledDiv sx={sx} />;
}

<DivWithSystemProps
  bgcolor="green"
  width={30}
  height={30}
  border="solid 1px red"
/>

说明

  • styled("div")(): 将 sx 属性添加到您的自定义组件中。
  • extendSxProp(props): 汇总顶级系统属性并将其放入 sx 属性中:
const props = { notSystemProps: true, color: 'green', bgcolor: 'red' };
const finalProps = extendSxProp(props);

// finalProps = {
//   notSystemProps: true,
//   sx: { color: 'green', bgcolor: 'red' }
// }

如果要在 TypeScript 中使用,您需要为所有系统属性添加类型:

type DivSystemProps = SystemProps<Theme> & {
  sx?: SxProps<Theme>;
};

function DivWithSystemProps(inProps: DivSystemProps) {
  const { sx, ...other } = extendSxProp(inProps);
  return <StyledDiv sx={sx} {...other} />;
}

Codesandbox Demo


非常感谢,这是我问题的最佳答案!! - Luan Cardoso

36

这是官方 Material-UI 演示文稿。

这里有个非常简单的例子。 它使用类似于 Styled Components 的语法:

import React from "react";
import { makeStyles, Button } from "@material-ui/core";

const useStyles = makeStyles({
  root: {
    background: props => props.color,
    "&:hover": {
      background: props => props.hover
    }
  },
  label: { fontFamily: props => props.font }
});

export function MyButton(props) {
  const classes = useStyles(props);
  return <Button className={classes.root} classes={{ label: classes.label }}>My Button</Button>;
}


// and the JSX...
<MyButton color="red" hover="blue" font="Comic Sans MS" />

这个演示使用了 makeStyles,但这个特性也可以在 styledwithStyles 中使用。

这个功能最初是在 2018年11月3日的 @material-ui/styles中引入的,并在 @material-ui/core 的版本4中包括了它。


由于我喜欢能够在属性级别上访问props,所以在我的情况下最好在样式级别上访问它const useStyles = (props) => makeStyles({})。我从服务器获取动态样式定义,我不知道那里定义了哪些CSS属性。在@material-ui/styles中是否有可能实现这一点? - Łukasz Jagodziński
@Jagi 由于makeStyles返回一个接受props并返回classes的函数,因此您可以始终将其包装在自己的自定义函数中,该函数接受props并返回classes。例如:const useStyles = (props) => { /* do stuff */ return makeStyles({}); }。这解决了您的问题吗?根据从服务器传来的props,您需要以何种方式更改传递给makeStyles的对象? - Dominus.Vobiscum
1
@Jagi 对不起,我的意思是这样的:const useStyles = (props, options) => { /* 做一些事情 */ return makeStyles({})(props, options); } - Dominus.Vobiscum
谢谢,它有效!我唯一担心的是,即使props没有改变,它也会在每次渲染时重新创建样式。或者makeStyles已经处理了这个问题? - Łukasz Jagodziński
没错, makeStyles 创建一个函数,并且该函数将在每次渲染时创建,而不是只创建一次。 然而,有两个想法:1)如果您传递给 makeStyles 的对象在每次渲染时都不同,那么就无法避免在每次渲染时创建新类(至少在当前 Material-UI 的功能下是如此),2)在您拥有指标证明性能对用户有影响之前,不必担心性能问题。 - Dominus.Vobiscum
显示剩余3条评论

25

此答案是在版本4.0之前编写的,已经过时!

如果你要为函数组件设置样式,请使用makeStyles

James Tan的答案是4.x版本最好的答案。

以下所有内容都已经过时:

当你使用withStyles时,可以访问theme,但无法访问props

请注意,在Github上有一个开放问题要求这个功能,一些评论可能会指向一种可能对你感兴趣的替代解决方案。

使用内联样式设置背景颜色的一种方法是将此属性设置为backgroundColor props。我对你的原始codesandbox进行了一些更改,你可以查看修改后的版本以查看它的运行情况。

这是我所做的:

  1. 使用backgroundColor props渲染组件:
// in index.js
if (rootElement) {
  render(<Demo backgroundColor="#f00" />, rootElement);
}
  1. 使用此属性将内联样式应用于卡片:
    function SimpleCard(props) {
      // in demo.js
      const { classes, backgroundColor } = props;
      const bull = <span className={classes.bullet}></span>;
      return (
        <div>
          <Card className={classes.card} style={{ backgroundColor }}>
            <CardContent>
              // etc

现在渲染的Card组件具有红色(#F00)背景。

查看文档的覆盖部分以获取其他选项。


@HugoGresse 谢谢!我稍微修改了你的编辑,指向更好的答案。 - Ken Gregory
请考虑改为其他答案之一。内联样式应该只作为最后的选择。 - Phil
@Phil 这也是我的建议。一段时间以前,我更新了答案,将观众引导到James Tan发布的答案。 - Ken Gregory

9

@mui v5

您可以使用styled()实用程序(确保您导入的是正确的),并使用shouldForwardProp选项。 在以下示例中,将SomeProps传递给一个div组件。

import { styled } from '@mui/material'

interface SomeProps {
  backgroundColor: 'red'|'blue',
  width: number
}
const CustomDiv  = styled('div', { shouldForwardProp: (prop) => prop !== 'someProps' })<{
  someProps: SomeProps;
}>(({ theme, someProps }) => {
  return ({
    backgroundColor: someProps.backgroundColor,
    width: `${someProps.width}em`,
    margin:theme.spacing(1)
  })
})

9

在这个帖子中缺少了一个在withStyles中使用props的例子(导致认为它不被支持)。

但是下面的代码对我有效(例如用于样式化MenuItem):

const StyledMenuItem = withStyles((theme) => ({
 root: {
  '&:focus': {
    backgroundColor: props => props.focusBackground,
    '& .MuiListItemIcon-root, & .MuiListItemText-primary': {
      color: props => props.focusColor,
    },
  },
 },
}))(MenuItem);

然后这样使用它:

 <StyledMenuItem focusColor={'red'} focusBackground={'green'}... >...</StyledMenuItem>

7
import React from "react";
import { makeStyles } from "@material-ui/styles";
import Button from "@material-ui/core/Button";

const useStyles = makeStyles({
  root: {
    background: props => props.color,
    "&:hover": {
      background: props => props.hover
    }
  }
});

export function MyButton(props) {
  const classes = useStyles({color: 'red', hover: 'green'});
  return <Button className={classes.root}>My Button</Button>;
}

4

我花了几个小时试图在TypeScript中传递属性时让withStyles正常工作。 我在网上找到的解决方案都不能满足我的需求,因此最终我从各处拼接出了自己的解决方案。

如果您有来自Material UI等外部组件,想要给它们提供默认样式,但同时也想通过传递不同的样式选项来重用它们,则应该可以使用此方法。

import * as React from 'react';
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles';
import { TableCell, TableCellProps } from '@material-ui/core';

type Props = {
    backgroundColor?: string
}

const useStyles = makeStyles<Theme, Props>(theme =>
    createStyles({
        head: {
            backgroundColor: ({ backgroundColor }) => backgroundColor || theme.palette.common.black,
            color: theme.palette.common.white,
            fontSize: 13
        },
        body: {
            fontSize: 12,
        },
    })
);

export function StyledTableCell(props: Props & Omit<TableCellProps, keyof Props>) {
    const classes = useStyles(props);
    return <TableCell classes={classes} {...props} />;
}

或许它不是完美的解决方案,但它似乎可以工作。真让人头疼的是他们没有将withStyles修改成可接受属性,这会让事情变得更容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接