Why is this happening?
If you have two CSS classes applied to the same element with the same degree of specificity, then the winner will be the CSS class that is defined last within the document (based on the order of the <style>
elements in the <head>
, NOT the order of the class name strings in the class
attribute of the element being styled).
This page is an example with two TextField elements that reproduces your problem. If you open the browser developer tools and look at the <style>
elements, you will see that the styles from makeStyles
come first followed by the styles from TextField
(e.g. MuiFormControl). I've shown an abbreviated version below:
<style data-jss="" data-meta="makeStyles">
.makeStyles-textFieldInput-1 {
margin: 32px;
min-width: 250px;
}
</style>
<style data-jss="" data-meta="MuiFormControl">
.MuiFormControl-root {
border: 0;
margin: 0;
display: inline-flex;
padding: 0;
position: relative;
min-width: 0;
flex-direction: column;
vertical-align: top;
}
.MuiFormControl-marginNormal {
margin-top: 16px;
margin-bottom: 8px;
}
.MuiFormControl-marginDense {
margin-top: 8px;
margin-bottom: 4px;
}
.MuiFormControl-fullWidth {
width: 100%;
}
</style>
<style data-jss="" data-meta="MuiTextField">
</style>
The MuiFormControl-root
class is applied to the same element as the class specified via TextField's className
property (e.g. the textFieldInput class from makeStyles/useStyles
). Since the MuiFormControl <style>
element occurs after the makeStyles
<style>
element, MuiFormControl's default styling for margin
and min-width
win over the custom styling specified by makeStyles
.
The order of these <style>
elements is controlled by the order in which makeStyles
is called. For the default styling for a given Material-UI component, makeStyles
is called at the point when the component is first imported.
For typical usage patterns, where makeStyles
is called in the same JavaScript file that then calls useStyles
and passes the classes to the Material-UI component, the order will always be what you would want because the imports of the Material-UI components will happen before the call to makeStyles
.
When you move the call to makeStyles
to a separate file and import the useStyles
method that it returns, you introduce the possibility of importing useStyles
before importing the Material-UI component (e.g. TextField in this case).
This is demonstrated in the code in this sandbox: https://codesandbox.io/s/makestyles-first-i1mwh
The reason it may work on the first hot reload is that the makeStyles
<style>
element is being removed and then added on to the end when you make changes. The Mui* style elements don't change so they remain where they are (which is before the new makeStyles
style element until the page is reloaded).
You can't easily shoot yourself in the foot this way using the Higher-order component API (i.e. withStyles
) since makeStyles
is called within withStyles
so you will have always imported the component being wrapped by withStyles
before passing it as a parameter.
How can I fix this?
There are a few ways you could address this. One way is to just ensure that you import your useStyles
function after importing Material-UI components such as TextField
.
Change:
import { useStyles } from "./styles";
import TextField from "@material-ui/core/TextField";
to instead be:
import TextField from "@material-ui/core/TextField";
import { useStyles } from "./styles";
This is demonstrated here: https://codesandbox.io/s/import-textfield-first-9qybd
This is fairly brittle however if you have styles for more than one type of component in styles.js
and import styles.js
from many files, since then for it to work reliably you are dependent on importing all of the Material-UI components that are styled by styles.js
before the first place that you import styles.js
.
Another way to address this is to export styled versions of the Material-UI components instead of exporting the useStyles
function. Then you just import this customized component instead of the Material-UI component.
import { withStyles } from "@material-ui/core/styles";
import MuiTextField from "@material-ui/core/TextField";
const styles = theme => ({
root: {
margin: theme.spacing(4),
minWidth: 250
}
});
export const TextField = withStyles(styles)(MuiTextField);
This is demonstrated with a couple different syntax options here: https://codesandbox.io/s/import-styled-textfield-1ytxl