Material UI 如何在事件触发时动态更新组件样式
MUI 事件触发动态更新样式解决方案
你之前的写法用的是静态样式定义,makeStyles和withStyles本身原生支持基于组件props动态生成样式,不需要在事件回调里手动修改生成好的CSS规则,只要把触发变更的状态纳入React的状态管理流,就能实现自动的样式更新。
核心原则
不要在React的生命周期外绑定事件后直接操作DOM样式或者JSS生成的样式表,这类手动修改会被React的重渲染覆盖,还会造成状态和UI不同步的问题。所有样式变更都通过状态驱动。
方案1:Hook 写法(makeStyles)实现
makeStyles支持传入函数形式的样式规则,函数入参就是你调用useStyles时传入的参数,基于这个参数就能返回不同场景下的样式:
import * as React from 'react'; import { makeStyles } from '@mui/styles'; import Button from '@mui/material/Button'; // 样式根据入参isDarkMode动态生成 const useStyles = makeStyles({ root: ({ isDarkMode }) => ({ background: isDarkMode ? 'linear-gradient(45deg, #1a237e 30%, #283593 90%)' : 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)', border: 0, borderRadius: 3, boxShadow: isDarkMode ? '0 3px 5px 2px rgba(26, 35, 126, .3)' : '0 3px 5px 2px rgba(255, 105, 135, .3)', color: 'white', height: 48, padding: '0 30px', }), }); export default function HookButton() { // 初始化时读取本地存储的主题配置 const [isDarkMode, setIsDarkMode] = React.useState( () => window.localStorage.getItem('theme') === 'dark' ); React.useEffect(() => { const handleStorageChange = () => { // storage事件触发时只更新React状态,不直接操作样式 setIsDarkMode(window.localStorage.getItem('theme') === 'dark'); }; window.addEventListener('storage', handleStorageChange); // 组件卸载时移除监听,避免内存泄漏 return () => window.removeEventListener('storage', handleStorageChange); }, []); // 把状态传给useStyles,状态变更时样式会自动重算 const classes = useStyles({ isDarkMode }); return <Button className={classes.root}>Hook</Button>; }
方案2:高阶组件写法(withStyles)实现
withStyles同样支持函数形式的样式规则,只需要把深色模式状态作为props传给包裹后的组件即可:
import * as React from 'react'; import PropTypes from 'prop-types'; import { withStyles } from '@mui/styles'; import Button from '@mui/material/Button'; const styles = { root: ({ isDarkMode }) => ({ background: isDarkMode ? 'linear-gradient(45deg, #1a237e 30%, #283593 90%)' : 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)', border: 0, borderRadius: 3, boxShadow: isDarkMode ? '0 3px 5px 2px rgba(26, 35, 126, .3)' : '0 3px 5px 2px rgba(255, 105, 135, .3)', color: 'white', height: 48, padding: '0 30px', }), }; function HigherOrderButton({ classes }) { return <Button className={classes.root}>Higher-order component</Button>; } HigherOrderButton.propTypes = { classes: PropTypes.object.isRequired, isDarkMode: PropTypes.bool.isRequired, }; const ThemedButton = withStyles(styles)(HigherOrderButton); // 外层组件负责管理状态和事件监听 export default function HOCWrapper() { const [isDarkMode, setIsDarkMode] = React.useState( () => window.localStorage.getItem('theme') === 'dark' ); React.useEffect(() => { const handleStorageChange = () => { setIsDarkMode(window.localStorage.getItem('theme') === 'dark'); }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); return <ThemedButton isDarkMode={isDarkMode} />; }
注意事项
storage事件只会在其他页面对localStorage做修改时触发到当前页面,如果是同页面的主题开关修改localStorage,直接在开关的回调里更新isDarkMode状态即可,不需要等storage事件。- 如果主题是全局配置,不需要每个组件都单独绑定storage事件,可以把
isDarkMode状态放在根组件或者React Context中,所有业务组件直接读取上下文里的状态传样式参数即可。 - 如果你用的是MUI v5及以上版本,更推荐用官方的
ThemeProvider+createTheme做全局深色模式切换,不需要每个组件单独写动态样式,直接在主题里配置调色板,所有MUI组件会自动适配。
内容的提问来源于stack exchange,提问作者AG_HIHI
相关产品推荐
相关产品推荐

