一般方法
通常在 MaterialUI 中,当我们想要覆盖有条件应用的 MaterialUI 样式时,我们可以使用 classes 属性:
classes={{ root: <class to apply always>, error: <class to apply in error state>}}
您可以在其 API 页面底部了解 CSS API 以及哪些类适用于给定组件。
或者,我们可以使用更精细的方法,并在组件处于错误状态时简单地应用规则:
className={ error ? <class to apply in error state> : undefined }
选择组件
Select 组件似乎是在与大多数其他 MaterialUI 输入组件不同的时间编写的。这是我基于 API 和代码风格的直观印象,但我可能错了。尽管如此,Select 组件使用Input、FilledInput 或OutlinedInput 之一,具体取决于variant 属性的值。由于您使用outlined,我将继续使用OutlinedInput 来回答这个问题。我还将使用绿色作为边框而不是橙色,因为它与红色的区别更明显。
天真地,我会通过查看 Select 组件的 CSS API 来解决这个问题,然后再仔细查看 OutlinedInput to which remaining properties of the Select component are spread。 Select 组件上不存在适用的 CSS API 类来实现您的目标,但 OutlinedInput 有一个类 error 具有以下描述:
Pseudo-class applied to the root element if error={true}.
如果我们在开发工具中检查OutlinedInput,我们可以看到有边框的元素是fieldset位于OutlinedInput的root元素内(注意:不是 Select 的 root 元素)。如果我们使用error 类,我们最终会得到一个附加类应用于OutlinedInput 的root 元素,这不是我们想要的。尽管如此,我们可以应用一个引用上述子元素的类......这会起作用。
还有一个 css 类 notchedOutline,我们可以看到它应用于这个 fieldset 导致边框。这里的困境是,如果我们能够向这个类添加一些 CSS,那么它会在fieldset 上所有时间应用,但我们希望仅应用它 处于错误状态。
这为我们提供了四种可能的策略来通过 CSS API 影响 css:
- 有条件地(出错时)将 css 添加到以子
notchedOutline 为目标的基础 OutlinedInput 的 root 类中。
- 将 css 添加到底层
OutlinedInput 的 root 类,该类以 error 状态和子 notchedOutline 为目标。
- 将css添加到底层
OutlinedInput和目标子notchedOutline的error类中。
- 有条件地(出错时)将 css 添加到底层
OutlinedInput 的 notchedOutline 类中。
请注意,第二个和第三个更可取,因为它们不需要我们有条件地添加一个类;这是由 MaterialUI 处理的。
在最好的情况下,我希望 Select 组件将其所有不存在的道具转发到它在内部使用的元素,在本例中为 OutlinedInput,但显然,它不会转发它部分支持的元素(classes 道具)。这意味着,如果我们提供classes.error 或classes.notchedOutline,MaterialUI 会报错
Material-UI: The key `XXXX` provided to the classes prop is not implemented in ForwardRef(Select).
而不是将道具转发到底层OutlinedInput。我不知道这是否是一个错误,但这就是它目前的工作方式。
这意味着我们不能通过Select 组件的classes 属性使用上述四种策略中的任何一种。一种解决方法是使用Select className 属性,该属性被转发到OutlinedInput,后者又将其转发到InputBase,后者将其转发到本机元素,这与root 的元素相同OutlinedInput。因此,如果我们可以访问底层的OutlinedInput 本身,我们可以使用上述所有策略,否则只能使用前两个(然后通过Select 的className 属性)。
解决方案
1.设置整个app的全局错误颜色
如果您希望整个应用程序的错误颜色不是红色,那么请提供一个主题来更改用于错误的颜色。这是最好的方法,因为它还会更改帮助文本和标签的颜色,否则它们的颜色会与 Select 输入的边框不同。
import green from '@material-ui/core/colors/green';
const theme = createTheme({
palette: {
error: {
main: green[500]
},
},
});
这是一种干净且简单的方法,也是我推荐的方法。但是,如果您不希望更改错误的一般颜色,这可能不是一个好的解决方案,即使您可以将应用程序的本地部分包装在它自己的主题中.
2.在错误状态下覆盖用于notchedOutline 的类
这是一种广泛的解决方法,因为您可以使用上述四种策略以几种不同的方式来解决此问题。所有解决方案的缺点是它们或多或少有点 hacky,并且它们只影响 Select 的边框,这意味着当组件处于错误状态时,帮助文本和标签的颜色与边框不同(你可以也给它们添加样式,使所有这些都非常复杂且难以实现)。
我们可以为OutlinedInput 创建一个具有全局覆盖的主题。由于我们随后会操纵OutlinedInput 本身,因此我们可以使用上面的所有四种策略。
只有当组件处于错误状态时,才必须有条件地应用此主题。这是精心设计的,不是一个好的选择。
const themeWithOverrides = createTheme({
overrides: {
MuiOutlinedInput: {
root: {
"&$error": {
"& $notchedOutline": {
border: "1px solid green"
}
}
}
}
}
})
这个效果很好,并针对 OutlinedInput root 类的 css,仅当它处于 error 状态时才适用,并适用于其 notchedOutline 孩子。选择器引用默认情况下已在该组件的主题中定义的 css 类。
此解决方案会导致来自 MaterialUI 的错误消息
Material-UI: The `MuiOutlinedInput` component increases the CSS specificity of the `error` internal state.
这反过来又建议我们使用之前针对 root 类的解决方案。
const themeWithOverrides = createTheme({
overrides: {
MuiOutlinedInput: {
notchedOutline: {
border: "1px solid green"
}
}
}
})
这将始终应用我们不想要的样式,因此必须有条件地应用主题,这很麻烦,因此根本不推荐这种解决方案。
2.b Select 组件的 CSS 类
理论上,我们也可以在这里使用上述所有四种策略。但是,由于我们没有操作 OutlinedInput 本身,并且由于 MaterialUI 不会将未为 Select 定义的 classes 属性转发到底层组件,因此只剩下两个。
const useStyles = makeStyles((theme) => ({
errorState: {
"& .MuiOutlinedInput-notchedOutline": {
border: "1px solid green !important"
}
}
}))
...
<Select
...
className={ errorFlag ? classes.errorState : undefined }
...
</Select>
此解决方案既使用文字 MaterialUI css 类,也需要我们有条件地添加该类。最后,我们必须使用 !important 修饰符来覆盖 MaterialUI 默认行为的特殊性。总而言之,这有点 hacky 的味道。
const useStyles = makeStyles((theme) => ({
errorState: {
"&.Mui-error": {
"& .MuiOutlinedInput-notchedOutline": {
border: "1px solid green"
}
}
}
}))
...
<Select
...
className={ classes.errorState }
...
</Select>
这稍微好一些,因为我们不需要有条件地应用样式。此外,只要在 MaterialUI 默认样式之后注入此样式,我们就可以省略 !important 修饰符,因为此规则实现了与 MaterialUI 默认行为相同的特异性。这种方法绝对优于以前的方法。
Select 组件采用一个 prop input,它允许您定义要使用的自己的输入组件。在这里,我们可以使用一个自定义组件,该组件返回一个 OutlinedInput 并进行您想要的修改。
export default function CustomInput(props) {
return (
<OutlinedInput
{...props}
<CHANGES HERE>
/>
)
}
由于我们在这里可以直接处理OutlinedInput,因此我们可以使用它的 CSS API,从而使用前面描述的所有四种策略。
请注意,要按本书进行,您应该将传入的classes.error 和classes.notchedOutline 与您默认分配的类合并。
有条件地将css类添加到classes.root prop定位子notchedOutline
由于我们现在可以访问 classes.error 属性,因此有条件地提供此类不是一个明智的选择。
将css类添加到classes.root道具定位状态error和子@ 987654446@
同样,由于我们现在可以访问 classes.error 属性,因此在此处提供一个以 error 状态为目标的类,而不是直接以它为目标,这将是一种额外的解决方法,不推荐使用。
将css类添加到classes.error prop定位子notchedOutline
const useStyles = makeStyles((theme) => ({
error: {
"& .MuiOutlinedInput-notchedOutline": {
border: "1px solid green !important"
}
}
}))
export default function CustomInput(props) {
const classes = useStyles()
return (
<OutlinedInput
{...props}
classes={{ ...props.classes, error: classes.error }}
/>
)
}
这是一个很好的替代方案,它允许我们将组件是否处于错误状态的决定留给 MaterialUI。使用文字 MaterialUI css 类有点 hacky,我们必须提供 !important 标志来覆盖 MaterialUI 默认行为的更高特异性。
在错误状态下有条件地将css类添加到classes.notchedOutline prop
const useStyles = makeStyles((theme) => ({
errorOutline: {
border: "1px solid green !important"
}
}))
export default function CustomInput(props) {
const classes = useStyles()
return (
<OutlinedInput
{...props}
classes={{ ...props.classes, notchedOutline: errorFlag ? classes.errorOutline : undefined }}
/>
)
}
这和上一个一样好。它使我们免于使用文字
MaterialUI css 类,但它迫使我们有条件地添加类。同样在这里,我们必须提供 !important 标志来覆盖 MaterialUI 默认行为的更高特异性。
结论
可惜这么简单的东西改起来好像挺复杂的。尽管如此,最好的解决方案似乎是:
如果您想为应用中的所有输入提供统一外观且与标签和帮助文本协调一致的稳健解决方案,请使用全局错误颜色覆盖。
import green from '@material-ui/core/colors/green';
const theme = createTheme({
palette: {
error: {
main: green[500]
},
},
})
如果您希望在所有 OutlinedInputs 中使用统一的错误颜色,并且您可以接受不协调的标签和帮助文本,请为 OutlinedInput 使用全局覆盖。
const themeWithOverrides = createTheme({
overrides: {
MuiOutlinedInput: {
root: {
"&$error": {
"& $notchedOutline": {
border: "1px solid green"
}
}
}
}
}
})
如果您需要一次性更改单个或多个Selects / OutlinedInputs 并且您可以接受标签,请使用自定义输入,当组件处于错误状态时有条件地覆盖classes.notchedOutline和颜色不协调的辅助文本。我更喜欢这个解决方案,因为我不喜欢在我的代码中使用文字 MaterialUI css 类。
const useStyles = makeStyles((theme) => ({
errorOutline: {
border: "1px solid green !important"
}
}))
export default function CustomInput(props) {
const classes = useStyles()
return (
<OutlinedInput
{...props}
classes={{ ...props.classes, notchedOutline: errorFlag ? classes.errorOutline : undefined }}
/>
)
}
...
<Select input={<CustomInput error={errorFlag} />} />
...
这是code sandbox with some of these recommended examples