样式被 Material-UI 样式覆盖

Jon*_*y B 6 javascript reactjs material-ui jss react-component

前言

几天前我问了一个类似的问题,虽然本质上是相关的,但我相信解决方案最终会有所不同,所以我在另一个线程中再次询问。

CodeSanbox示例(已更新以反映已接受的答案)

问题:

我希望通过classNameprop 传入的任何外部样式都比我的自定义组件内部样式具有更高的特异性。这样,使用它的人就可以调整边距和填充。但是,我的组件默认内部样式正在覆盖我的外部样式,我希望它是相反的。

你可以看到我的外部风格特异性较低

细节:

我正在创建一个基于material-ui 之上的自定义组件库。我想让自定义组件 api 类似于@material-ui这样我们的开发人员会发现它们更易于使用。我正在构建的每个组件都有自己的内部样式,覆盖默认的material-ui样式,在本例中它被定义为 class button。另外,就像@material-ui我接受一个 color prop一样<TestButton color={'default'}/>。最后,如果需要的话,我希望允许用外部样式覆盖我的自定义按钮。我正在使用该clsx库来构建 className 字符串。

代码:

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

笔记:

我在这个问题和代码沙箱示例中大大简化了代码。请不要因为这个例子而评论说你认为我所做的事情没有意义。

Rya*_*ell 3

来自https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

当多个声明具有相同的特异性时,CSS 中找到的最后一个声明将应用于该元素。

因此,如果您在自定义组件(例如TestButton)和使用该组件的代码中定义 CSS 类,则特殊性取决于这些 CSS 类在元素中出现的顺序<head>此顺序由调用 makeStyles 时设置的索引确定,因此稍后调用定义的类将makeStyles稍后出现在<head>元素中,因此具有更大的特异性。

在您的示例中存在两个问题:

  1. TestButton是在使用它的代码之后定义的,因此makeStyles是在定义旨在覆盖TestButton. 由于首先发生makeStyles调用gButton,因此相应的 CSS 类将位于<head>元素中的第一个。但在实际使用中,TestButton(您的自定义组件)将在单独的文件中定义并导入。由于导入必须位于顶部,因此导入文件顶层makeStyles的任何调用都将在使用导入组件的文件中的任何调用之前执行。makeStyles

  2. 这一makeStyles呼吁TestButton并未在最高层进行相反,它是在函数内部完成的TestButton,这意味着它将在渲染时执行TestButton,而不是在TestButton导入时执行。对的调用makeStyles应始终位于顶层,而不是嵌套在组件函数中。另一个小问题是返回的变量的名称makeStyles(即GrangeButtonStyles在您的示例中)。由于makeStyles返回自定义钩子,因此您应该始终拥有以“use”开头的名称(例如useGrangeButtonStyles)。这将确保钩子的 eslint 规则将其识别为钩子,并警告您任何钩子误用。

相关回答及参考: