Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override classes using makeStyles and useStyles in material-ui?

Consider a component that renders a button and says this button should have a red background and a yellow text color. Also there exists a Parent component that uses this child but says, the yellow color is fine, but I want the background color to be green.

withStyles

No problem using the old withStyles.

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

const parentStyles = {
  root: {
    background: "green"
  }
};
const childStyles = {
  root: {
    background: "red"
  },
  label: {
    color: "yellow"
  }
};

const ChildWithStyles = withStyles(childStyles)(({ classes }) => {
  return <Button classes={classes}>Button in Child withStyles</Button>;
});

const ParentWithStyles = withStyles(parentStyles)(({ classes }) => {
  return <ChildWithStyles classes={classes} />;
});


export default ParentWithStyles;

Button in child using withStyles

https://codesandbox.io/s/passing-classes-using-withstyles-w17xs?file=/demo.tsx

makeStyles/useStyles

Let's try the makeStyles/useStyles instead and follow the guide Overriding styles - classes prop on material-ui.com.

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

const parentStyles = {
  root: {
    background: "green"
  }
};
const childStyles = {
  root: {
    background: "red"
  },
  label: {
    color: "yellow"
  }
};

// useStyles variant does NOT let me override classes
const useParentStyles = makeStyles(parentStyles);
const useChildStyles = makeStyles(childStyles);

const ChildUseStyles = ({ classes: classesOverride }) => {
  const classes = useChildStyles({ classes: classesOverride });
  return (
    <>
      <Button classes={classes}>Button1 in Child useStyles</Button>
      <Button classes={classesOverride}>Button2 in Child useStyles</Button>
    </>
  );
};
const AnotherChildUseStyles = props => {
  const classes = useChildStyles(props);
  return (
    <>
      <Button classes={classes}>Button3 in Child useStyles</Button>
    </>
  );
};
const ParentUseStyles = () => {
  const classes = useParentStyles();
  return <>
    <ChildUseStyles classes={classes} />
    <AnotherChildUseStyles classes={classes} />
  </>
};

export default ParentUseStyles;

enter image description here

https://codesandbox.io/s/passing-classes-using-usestyles-6x5hf?file=/demo.tsx

There seems no way to get the desired effect that I got using withStyles. A few questions, considering I still want the same effect (green button yellow text) using some method of classes overriding (which seemed to make sense to me before).

  • How is my understanding wrong about how to pass classes as means to override parts of them using useStyles?
  • How should I approach it alternatively?
  • And if I'm using the wrong approach, why is material-ui still giving me a warning when the parent has something in the styles that the child doesn't have?

    the key something provided to the classes prop is not implemented in [Child]

  • Is the migration from the old approach (withStyles) vs the new approach documented somewhere?

Btw, I'm aware of this solution but that seems cumbersome when you have too much you want to override.

const useStyles = makeStyles({
  root: {
    backgroundColor: 'red',
    color: props => props.color, // <-- this
  },
});

function MyComponent(props) {
  const classes = useStyles(props);
  return <div className={classes.root} />;
}
like image 786
Christiaan Westerbeek Avatar asked May 01 '20 13:05

Christiaan Westerbeek


People also ask

How do you override mui styles?

The second way to override the style of a component is to use the inline-style approach. Every component provides a style property. These properties are always applied to the root element. You don't have to worry about CSS specificity as the inline-style takes precedence over the regular CSS.

How do you use makeStyles in material UI?

In order to use makeStyles function in your application you will need to import it in the component in where you plan to access the css that results from your makeStyles function. This is assuming you have already installed Material Ui in your project. This is in order for react to recognize it as a styles file.

Is MUI makeStyles deprecated?

It uses a syntax called CSS-in-JS (or JSS) which is just a JavaScript-friendly way of writing styles that compile into actual browser-readable CSS. Though deprecated, you can still use makeStyles() in MUI 5, but we expect it (to be removed in v6, but hence my confusion between past and present tense).


1 Answers

withStyles has very little functionality in it. It is almost solely a wrapper to provide an HOC interface to makeStyles / useStyles. So all of the functionality from withStyles is still available with makeStyles.

The reason you aren't getting the desired effect is simply because of order of execution.

Instead of:

const useParentStyles = makeStyles(parentStyles);
const useChildStyles = makeStyles(childStyles);

you should have:

const useChildStyles = makeStyles(childStyles);
const useParentStyles = makeStyles(parentStyles);

Edit Passing classes using useStyles

The order in which makeStyles is called determines the order of the corresponding style sheets in the <head> and when specificity is otherwise the same, that order determines which styles win (later styles win over earlier styles). It is harder to get that order wrong using withStyles since the wrapper that you are using to override something else will generally be defined after the thing it wraps. With multiple calls to makeStyles it is easier to do an arbitrary order that doesn't necessarily put the overrides after the base styles they should impact.

The key to understanding this is to recognize that you aren't really passing in overrides, but rather a set of classes to be merged with the new classes. If childClasses.root === 'child_root_1' and parentClasses.root === 'parent_root_1', then the merged result is mergedClasses.root === 'child_root_1 parent_root_1' meaning any elements that have their className set to mergedClasses.root are receiving both CSS classes. The end result (as far as what overrides what) is fully determined by CSS specificity of the styles in the two classes.

Related answers:

  • Material UI v4 makeStyles exported from a single file doesn't retain the styles on refresh
  • Internal implementation of "makeStyles" in React Material-UI?
like image 84
Ryan Cogswell Avatar answered Sep 28 '22 05:09

Ryan Cogswell