useMediaQuery
这个 React Hook 监听 CSS 媒体查询的匹配情况。它允许根据查询是否匹配来渲染组件。
一些主要特性
- ⚛️ 它具有符合语言习惯的 React API。
- 🚀 它性能卓越,它观察文档以检测其媒体查询何时更改,而不是定期轮询值。
- 📦 1.1 kB gzipped。
- 🤖 它支持服务端渲染。
基本媒体查询
您应该将媒体查询提供给 Hook 的第一个参数。媒体查询字符串可以是任何有效的 CSS 媒体查询,例如 '(prefers-color-scheme: dark)'
。
⚠️ 由于浏览器的限制,您不能使用 'print'
,例如 Firefox。
使用断点助手
您可以如下使用 Material UI 的 断点助手
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up('sm'));
return <span>{`theme.breakpoints.up('sm') matches: ${matches}`}</span>;
}
或者,您可以使用回调函数,接受 theme 作为第一个参数
import useMediaQuery from '@mui/material/useMediaQuery';
function MyComponent() {
const matches = useMediaQuery((theme) => theme.breakpoints.up('sm'));
return <span>{`theme.breakpoints.up('sm') matches: ${matches}`}</span>;
}
⚠️ 没有默认主题支持,您必须将其注入到父主题提供器中。
使用 JavaScript 语法
您可以使用 json2mq 从 JavaScript 对象生成媒体查询字符串。
测试
您的测试环境中需要 matchMedia 的实现。
例如,jsdom 尚不支持它。您应该对其进行 polyfill。建议使用 css-mediaquery 来模拟它。
import mediaQuery from 'css-mediaquery';
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addEventListener: () => {},
removeEventListener: () => {},
});
}
describe('MyTests', () => {
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
});
仅客户端渲染
为了执行服务端 hydration,Hook 需要渲染两次。第一次使用 defaultMatches
,即服务器的值,第二次使用已解析的值。这种双程渲染周期有一个缺点:它速度较慢。如果您仅在客户端使用返回值,则可以将 noSsr
选项设置为 true
。
const matches = useMediaQuery('(min-width:600px)', { noSsr: true });
或者可以通过主题全局启用它
const theme = createTheme({
components: {
MuiUseMediaQuery: {
defaultProps: {
noSsr: true,
},
},
},
});
服务端渲染
首先尝试依赖客户端 CSS 媒体查询。例如,您可以使用
如果以上替代方案都不可行,您可以继续阅读文档的此部分。
首先,您需要从服务器猜测客户端请求的特征。您可以在使用以下方式之间进行选择
- 用户代理。解析客户端的用户代理字符串以提取信息。建议使用 ua-parser-js 来解析用户代理。
- 客户端提示。读取客户端发送给服务器的提示。请注意,此功能并非在所有地方都受支持。
最后,您需要为 useMediaQuery
提供 matchMedia 的实现,并带有先前猜测的特征。建议使用 css-mediaquery 来模拟 matchMedia。
例如,在服务端
import * as ReactDOMServer from 'react-dom/server';
import parser from 'ua-parser-js';
import mediaQuery from 'css-mediaquery';
import { createTheme, ThemeProvider } from '@mui/material/styles';
function handleRender(req, res) {
const deviceType = parser(req.headers['user-agent']).device.type || 'desktop';
const ssrMatchMedia = (query) => ({
matches: mediaQuery.match(query, {
// The estimated CSS width of the browser.
width: deviceType === 'mobile' ? '0px' : '1024px',
}),
});
const theme = createTheme({
components: {
// Change the default options of useMediaQuery
MuiUseMediaQuery: {
defaultProps: {
ssrMatchMedia,
},
},
},
});
const html = ReactDOMServer.renderToString(
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>,
);
// …
}
确保您为客户端提供相同的自定义 matchMedia 实现,以保证 hydration 匹配。
从 withWidth()
迁移
withWidth()
高阶组件注入页面的屏幕宽度。您可以使用 useWidth
Hook 重现相同的行为
API
useMediaQuery(query, [options]) => matches
参数
query
(string | func): 表示要处理的媒体查询的字符串,或接受 theme(在上下文中)并返回字符串的回调函数。options
(object [可选])
options.defaultMatches
(bool [可选]): 由于window.matchMedia()
在服务器上不可用,因此它在首次挂载期间返回默认匹配项。默认值为false
。options.matchMedia
(func [可选]): 您可以提供自己的 matchMedia 实现。这可以用于处理 iframe 内容窗口。options.noSsr
(bool [可选]): 默认为false
。为了执行服务端 hydration,Hook 需要渲染两次。第一次使用defaultMatches
,即服务器的值,第二次使用已解析的值。这种双程渲染周期有一个缺点:它速度较慢。如果您仅在客户端使用返回值,则可以将此选项设置为true
。options.ssrMatchMedia
(func [可选]): 您可以提供自己的 matchMedia 实现,它在服务端渲染时使用。
注意:您可以使用主题的 default props
功能和 MuiUseMediaQuery
键来更改默认选项。
返回值
matches
: 如果文档当前匹配媒体查询,则 Matches 为 true
,否则为 false
。
示例
import * as React from 'react';
import useMediaQuery from '@mui/material/useMediaQuery';
export default function SimpleMediaQuery() {
const matches = useMediaQuery('(min-width:600px)');
return <span>{`(min-width:600px) matches: ${matches}`}</span>;
}