如何在Next.js的styled-components主题中使用window.matchMedia实现响应式更新?
问题原因
你定义的theme是静态常量,isCompact属性仅在模块导入时计算一次,后续窗口尺寸变化不会更新该值,ThemeProvider也不会触发下游组件重渲染,因此样式无法同步更新。
解决步骤
- 将
isCompact改为响应式状态,在_app.js中监听媒体查询变化同步更新状态,再将动态状态注入ThemeProvider即可。修改后的_app.js代码如下:
import { ThemeProvider } from 'styled-components'; import { useState, useEffect } from 'react'; // 断点常量可以抽成单独的配置文件统一管理 const smartPhonePortrait = 320; const App = ({ Component, pageProps, router }) => { // 服务端渲染阶段默认赋值false,避免 hydration 不匹配 const [isCompact, setIsCompact] = useState(false); useEffect(() => { const mediaQuery = window.matchMedia(`(min-width:${smartPhonePortrait}px)`); // 初始化赋值当前媒体查询匹配结果 setIsCompact(mediaQuery.matches); // 定义媒体查询变化的回调函数 const handleMediaChange = (e) => { setIsCompact(e.matches); }; // 绑定监听事件 mediaQuery.addEventListener('change', handleMediaChange); // 组件卸载时移除监听,避免内存泄漏 return () => { mediaQuery.removeEventListener('change', handleMediaChange); }; }, []); const theme = { isCompact }; return ( <ThemeProvider theme={theme}> <Component {...pageProps} /> </ThemeProvider> ); }; export default App;
- 原来的静态
theme.js文件可以删除,断点常量如果需要多处使用,可以单独抽成constants/breakpoints.js导出复用即可。
额外注意
你当前使用的是min-width:320px作为判断条件,即视口宽度≥320px时isCompact为true。如果你期望的是「窄屏移动端(宽度小于320px)使用100%宽度」,需要将媒体查询修改为max-width:${smartPhonePortrait}px。
内容的提问来源于stack exchange,提问作者CodingLittle
相关产品推荐
相关产品推荐

