从单个文件导出的材质 UI v4 makeStyles 在刷新时不会保留样式



我正在使用材质UI v4,我正在从单个文件中导出我的样式, 但是这些样式在其他组件中不起作用 样式.js

const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
// textField component styles
textFieldInput: {
margin: theme.spacing(2),
width: 250,
minWidth: 250,
},
formControl: {
margin: theme.spacing(2),
minWidth: 120,
},

})
export {useStyles}

在我的组件文件中

....
const classes = useStyles(styles);
return (
<TextField
className={classes.textFieldInput}
label={label}
placeholder={label}
error={touched && invalid}
helperText={touched && error}
{...input}
disabled={disabled || false}
readOnly={readOnly || false}
required={required || false}
InputProps={{ readOnly, ...custom }}
{...custom}
/>
);
....

当我在我的组件中使用它时 样式将在第一次热重载时工作,但之后样式将没有任何影响,为什么?以及我如何解决这个问题

为什么会这样?

如果您将两个 CSS 类应用于具有相同特异性程度的同一元素,则获胜者将是文档中最后定义的 CSS 类(基于<head><style>元素的顺序,而不是被样式元素的class属性中类名字符串的顺序)。

此页面是一个示例,其中包含两个重现您的问题的 TextField 元素。如果您打开浏览器开发人员工具并查看<style>元素,您将看到来自makeStyles的样式排在第一位,然后是来自TextField的样式(例如MuiFormControl)。我在下面展示了一个缩写版本:

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

MuiFormControl-root类应用于与通过 TextField 的className属性指定的类相同的元素(例如,来自makeStyles/useStyles的 textFieldInput 类)。由于 MuiFormControl<style>元素出现在makeStyles<style>元素之后,因此 MuiFormControl 的默认样式marginmin-width胜过makeStyles指定的自定义样式。

这些<style>元素的顺序由调用makeStyles的顺序控制。对于给定 Material-UI 组件的默认样式,makeStyles在首次导入组件时调用

。对于典型的使用模式,其中makeStyles在同一个 JavaScript 文件中调用,然后调用useStyles并将类传递给 Material-UI 组件,顺序将始终是您想要的,因为 Material-UI 组件的导入将在调用makeStyles之前发生。

当您将调用移动到makeStyles到单独的文件并导入它返回的useStyles方法时,您将引入在导入 Material-UI 组件(例如本例中的 TextField)之前导入useStyles的可能性。

此沙盒中的代码演示了这一点:https://codesandbox.io/s/makestyles-first-i1mwh

它可能在第一次热重载时起作用的原因是,当您进行更改时,makeStyles<style>元素将被删除,然后添加到末尾。Mui* 样式元素不会更改,因此它们会保留在原处(即在重新加载页面之前的新makeStyles样式元素之前)。

你不能使用高阶组件 API 以这种方式轻松地搬起石头砸自己的脚(即withStyles),因为makeStyles是在withStyles中调用的,因此在将组件作为参数传递之前,您将始终导入被withStyles包装的组件。


我该如何解决这个问题?

有几种方法可以解决此问题。一种方法是确保在导入 Material-UI 组件(如TextField)导入useStyles函数。

改变:

import { useStyles } from "./styles";
import TextField from "@material-ui/core/TextField";

改为:

import TextField from "@material-ui/core/TextField";
import { useStyles } from "./styles";

此处演示了这一点:https://codesandbox.io/s/import-textfield-first-9qybd

这是相当脆弱的,但是,如果您在styles.js中具有多种类型的组件的样式并从许多文件中导入styles.js,因为从那时起,要使其可靠地工作,您需要在导入styles.js的第一个位置之前导入所有由styles.js设置样式的 Material-UI 组件。


解决此问题的另一种方法是导出 Material-UI 组件的样式版本,而不是导出useStyles函数。然后,您只需导入此自定义组件而不是材质 UI 组件。

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

这里用几个不同的语法选项来演示这一点:https://codesandbox.io/s/import-styled-textfield-1ytxl

我一直面临类似的问题,并设法解决如下

root: {
'&&': {
width: "128px",
height: "128px",
margin: "8px",
}
},

我后来也在这里找到了一篇关于这个的文章。

使用双与号"&&"会增加/加倍特异性/优先级。 因此,对于我想要覆盖的任何类,我在声明的类中添加了"&&"。

这为我解决了问题,没有任何明显的缺点,但我不知道这是否真的是一种好的做法。如果有人知道更多为什么不使用它,请告诉我。

最新更新