前言
几天前我问过similar question,虽然本质上是相关的,但我认为解决方案最终会有所不同,因此我在不同的线程中再次提出问题。
CodeSanbox Example
问题:
我希望通过className
道具传递的任何外部样式都比我的自定义组件内部样式具有更高的特异性。这样,使用它的人就可以调整边距和填充。但是,我组件的默认内部样式会覆盖我的外部样式,我希望反之亦然。
详细信息:
我正在创建一个基于material-ui的自定义组件库。我想使自定义组件api与@material-ui
类似,以便我们的开发人员可以更轻松地使用它们。我正在构建的每个组件都具有其自己的内部样式,该样式将覆盖默认的material-ui样式,在这种情况下,它定义为类button
。另外,像@material-ui
一样,我接受颜色道具<TestButton color={'default'}/>
。最后,如果需要,我希望可以使用外部样式覆盖自定义按钮。我正在使用clsx
库来构建className字符串。
The代码:
import React, { useState } from "react";
import { makeStyles } from "@material-ui/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import { Button } from "@material-ui/core";
import clsx from "clsx";
const useAppStyles = makeStyles({
gButton: { margin: "150px" }
});
export default function App() {
const classes = useAppStyles();
return (
<div className={classes.example}>
<div className={classes.separator}>
<div>Buttons:</div>
<TestButton
className={classes.gButton}
color={"default"}
>
Default
</TestButton>
<TestButton
className={classes.gButton}
color={"primary"}
>
Primary
</TestButton>
</div>
);
}
function TestButton(props) {
const classes = GrangeButtonStyles();
let color = props.color === 'default' ? classes.default : classes.primary
const GrangeButtonStyles = makeStyles({
button: {
height: "45px",
padding: "13px 30px 13px 30px",
borderRadius: "5px",
border: "none",
margin: "15px",
},
default: {
backgroundColor: "black",
border: 'solid #2e7d32 1px',
color: "white",
},
primary: {
backgroundColor: 'white',
color: 'black',
fontFamily: 'Montserrat, sans-serif',
border: 'solid black 1px',
}
});
return (
<Button
className={clsx(classes.button, color, props.className)}
variant="contained"
disabled={props.disabled}
disableElevation
>
{props.children}
</Button>
);
}
注意:
在这个问题和代码沙箱示例中,我已经大大简化了空间代码。请不要发表评论,因为这个例子,您认为我在做什么没有意义。
<TestButton className={classes.gButton} color={"default"}>
// should be
<TestButton classes={{button:classes.gButton}} color={"default"}>
// and
<TestSelect className={classes.gSelect}
// should be
<TestSelect className={{dropdown:classes.gSelect}}
^在处理material-ui的样式解决方案时,不要将“ className”传递给组件(仅将此道具放在DOM元素上!!!]
和
function TestButton(props) {
const classes = GrangeButtonStyles();
...
// should be
function TestButton(props) {
const classes = GrangeButtonStyles(props);
...
^这将导致prop classes.button(看起来像jss-1233
)与GrangeButtonStyles
中出现的按钮类合并,因此类现在看起来像这样:
{
button: 'jss-7382 jss-1233' <- jss-1233 is the classname that got generated in the previous component
}
和
<Button
className={clsx(classes.button, color, props.className)}
// should be
<Button
classes={{root:classes.button)}
^ See material-ui docs for button
不幸的是,material-ui将其引用转发给DOM元素而无需检查className,因为这允许人们将className放在material-ui组件上,并且它“有点像”工作。他们应该真正添加警告以改为使用感谢您的评论员纠正了我的错误,我仍然喜欢传递classes
。classes
而不是className的冗长,因为将两者混合会导致混淆!
编辑:
[我也注意到您在TestSelect
中弄乱了样式-始终牢记-不要将className
作为props-ui组件的道具,只能通过classes
。如果要将样式从父组件传递到子组件,则必须使用相同的键:
让我们尝试一个例子,我知道,这些东西很难理解,但最终它会“点击”:
const useParentStyles = makeStyles({
childStyles: { ... some jss }
childStyles2: { ... some jss }
});
const Parent = props => {
const classes = useParentStyles(props);
return <Child classes={{root:classes.childStyles,someOtherKey:classes.childStyles2}}/> <- object with a keys of "root" and "someOtherKey"
}
const useChildStyles = makeStyles({
root: { ... some jss } <- root
someOtherKey: { ... some jss } <- someOtherKey
});
const Child = props => {
const classes = useChildStyles(props); <- classes have been merged together
return <div className={classes.root}>...</div>
}