简体   繁体   English

样式被 Material-UI 样式覆盖

[英]Styles being overwritten by Material-UI style

Preface前言

I asked a similar question to this several days back and while related in nature I believe the solution will ultimately be different, so I am asking again in a different thread.几天前我问了一个类似的问题,虽然本质上是相关的,但我相信解决方案最终会有所不同,所以我在不同的线程中再次提问

CodeSanbox Example (Has Been updated to reflect the accepted answer) CodeSanbox 示例(已更新以反映已接受的答案)

The issue:问题:

I'd like any external styles passed in with the className prop to have higher specificity than my custom components internal style.我希望通过className传入的任何外部样式都比我的自定义组件内部样式具有更高的特异性。 That way someone using it can adjust margins and padding.这样,使用它的人可以调整边距和填充。 However, my components default internal style is overwriting my external style and I would like it to be the other way around.但是,我的组件默认内部样式覆盖了我的外部样式,我希望它是相反的。

你可以看到我的外部风格是低规格的

The Details:细节:

I am creating a custom component library built on top of material-ui.我正在创建一个构建在 material-ui 之上的自定义组件库。 I'd like to make the custom components api similar to @material-ui so that our devs will find them easier to use.我想让自定义组件 api 类似于@material-ui以便我们的开发人员发现它们更易于使用。 Each component I am building has it's own internal style overwriting the default material-ui styles in this case it is defined as class button .我正在构建的每个组件都有自己的内部样式,覆盖默认的 material-ui 样式,在这种情况下,它被定义为 class button Additionally, like @material-ui I am accepting a color prop <TestButton color={'default'}/> .此外,像@material-ui我接受一个颜色道具<TestButton color={'default'}/> Finally, I'd like my custom button to be allowed to be overwritten with external styles if the need ever arises.最后,如果需要,我希望我的自定义按钮可以被外部样式覆盖。 I am using the clsx library to build the className strings.我正在使用clsx库来构建 className 字符串。

The Code:编码:

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>
  );
}

NOTE:笔记:

I have simplified the code greatly for space in this question and in the code sandbox example.我在这个问题和代码沙箱示例中大大简化了代码。 Please don't comment that you think what I'm doing doesn't make sense because of the example.请不要因为这个例子而评论你认为我正在做的事情没有意义。

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

When multiple declarations have equal specificity, the last declaration found in the CSS is applied to the element.当多个声明具有相同的特异性时,CSS 中找到的最后一个声明将应用于元素。

So in your case where you are defining CSS classes in your custom component (eg TestButton ) and in the code that uses that component, the specificity is determined by the order in which those CSS classes appear within the <head> element.因此,在您在自定义组件(例如TestButton )和使用该组件的代码中定义 CSS 类的情况下,特异性取决于这些 CSS 类在<head>元素中出现的顺序。 This order is determined by an index that is set when makeStyles is called , so classes defined by later calls to makeStyles will appear later in the <head> element and thus have greater specificity.此顺序由调用makeStyles 时设置索引确定,因此稍后调用makeStyles定义的类将稍后出现在<head>元素中,因此具有更大的特异性。

There are two issues then in your example:在您的示例中有两个问题:

  1. TestButton is defined after the code that uses it and therefore after the makeStyles call that is defining styles intended to override styles in TestButton . TestButton在使用它的代码之后定义,因此在定义旨在覆盖TestButton样式的样式的makeStyles调用之后定义。 Since the makeStyles call for gButton occurs first, the corresponding CSS class will be first in the <head> element.由于对makeStyles调用gButton发生,相应的 CSS 类将首先出现在<head>元素中。 In real-world usage though, TestButton (your custom component) would be defined in a separate file and be imported.但在实际使用中, TestButton (您的自定义组件)将在单独的文件中定义并导入。 Since imports have to be at the top, any makeStyles calls at the top level of the imported file will be executed before any makeStyles calls in the file using the imported component.由于进口必须是在顶部,任何makeStyles导入文件的顶层通话将之前的任何执行makeStyles采用进口组合在文件中调用。

  2. The makeStyles call for TestButton is not being done at the top level.makeStyles呼吁TestButton在顶层完成。 Instead it is being done inside the TestButton function which means it will be executed when TestButton is rendered instead of when TestButton is imported.相反,它是在TestButton函数内部完成的,这意味着它将在呈现TestButton时执行,而不是在导入TestButtonTestButton Calls to makeStyles should always be at the top level rather than nested within a component function.makeStyles调用应始终位于顶层,而不是嵌套在组件函数中。 One other minor issue is the name of the variable returned from makeStyles (ie GrangeButtonStyles in your example).另一个小问题是从makeStyles返回的变量的名称(即GrangeButtonStyles中的GrangeButtonStyles )。 Since makeStyles returns a custom hook , you should always have a name that starts with "use" (eg useGrangeButtonStyles ).由于makeStyles返回一个自定义钩子,因此您应该始终有一个以“use”开头的名称(例如useGrangeButtonStyles )。 This will ensure that the eslint rules for hooks recognize it as a hook and warn you of any hook misuse.这将确保钩子eslint 规则将其识别为钩子并警告您任何钩子误用。

Related answers and references:相关回答和参考:

<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}}

^ when dealing with material-ui's styling solution, don't pass "className" to components (only put this prop on DOM elements!!!!) ^ 在处理 material-ui 的样式解决方案时,不要将 "className" 传递给组件(仅将此 prop 放在 DOM 元素上!!!!)

and

function TestButton(props) {
  const classes = GrangeButtonStyles();
  ...

// should be


function TestButton(props) {
  const classes = GrangeButtonStyles(props);
  ...


^ This will cause the prop classes.button (which will look like jss-1233 ) to be merged with the button class that comes out of GrangeButtonStyles so classes will now look like this: ^ 这将导致prop classes.button (看起来像jss-1233 )与来自GrangeButtonStyles的按钮类合并,所以类现在看起来像这样:

{
  button: 'jss-7382 jss-1233' <- jss-1233 is the classname that got generated in the previous component
}

and

    <Button
      className={clsx(classes.button, color, props.className)}

// should be

    <Button
      classes={{root:classes.button)}

^ See material-ui docs for button ^有关按钮,请参阅 material-ui 文档

It's actually unfortunate that material-ui forwards it's refs to the DOM element without checking for className because this allows people to put className on material-ui components and it "kind of" work.实际上不幸的是,material-ui 将它的引用转发到 DOM 元素而不检查 className,因为这允许人们将 className 放在 material-ui 组件上并且它“有点”工作。 They should really add warnings to use classes instead.他们真的应该添加警告来代替使用classes Thanks for a commentor for correcting my mistake, I still like the verbosity of passing classes instead of className as mixing the two can result in confusion!感谢评论者纠正我的错误,我仍然喜欢传递classes而不是 className 的冗长性,因为将两者混合会导致混淆!

EDIT:编辑:

I also noticed you're messing up the styles in TestSelect - always keep this in mind - don't pass className as props to material-ui components, only pass classes .我还注意到您在TestSelect弄乱了样式 - 始终牢记这一点 - 不要将className作为 props 传递给 material-ui 组件,只传递classes If you want to pass styles from a parent component to a child component then you've got to use the same key:如果要将样式从父组件传递给子组件,则必须使用相同的键:

Let's try an example, I know, this stuff is hard to grok but eventually it will "click":让我们尝试一个例子,我知道,这个东西很难理解,但最终它会“点击”:

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>
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM