Reputation: 11137
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.
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;
https://codesandbox.io/s/passing-classes-using-withstyles-w17xs?file=/demo.tsx
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;
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).
the key
something
provided to the classes prop is not implemented in [Child]
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} />;
}
Upvotes: 10
Views: 15167
Reputation: 6232
One way to achieve this using withStyles
is the following and can be helpful to override css classes.
Supposing that you want to override a class called ".myclass
" which contains "position: absolute;"
:
import { withStyles } from '@material-ui/styles';
const styles = {
"@global": {
".myClass": {
position: "relative",
}
}
};
const TestComponent = (props) => (
<>
<SomeComponent {...props}>
</>
);
export default withStyles(styles)(TestComponent);
After doing this, you override the definition of .myClass
defined on <SomeComponent/>
to be "position: relative;"
.
Upvotes: 0
Reputation: 1582
In Material-ui 4.11.x while creating styles using makeStyles wrap the enclosing styles with createStyles, and this style will have highest priority than the default one.
const useStyles = makeStyles((theme: Theme) =>
createStyles({
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: '#fff',
},
}),
);
You could try removing the createStyles and see the difference. code source from https://material-ui.com/components/backdrop/
Upvotes: 0
Reputation: 80966
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);
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:
Upvotes: 6