useMediaQuery
這個 React Hook 監聽 CSS media query 的匹配。它允許根據查詢是否匹配來渲染元件。
一些主要功能
- ⚛️ 它具有符合 React 風格的 API。
- 🚀 效能良好,它觀察文件以檢測其 media query 何時更改,而不是定期輪詢值。
- 📦 1.1 kB gzip 壓縮後。
- 🤖 它支援伺服器端渲染。
基本 media query
您應該將 media query 提供給 Hook 的第一個參數。media query 字串可以是任何有效的 CSS media query,例如 '(prefers-color-scheme: dark)'
。
⚠️ 由於瀏覽器的限制,您不能使用 'print'
,例如 Firefox。
使用 breakpoint helpers
您可以如下使用 Material UI 的 breakpoint helpers
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>;
}
⚠️ 沒有預設的 theme 支援,您必須將其注入到父層 theme provider 中。
使用 JavaScript 語法
您可以使用 json2mq 從 JavaScript 物件產生 media query 字串。
測試
您需要在您的測試環境中實作 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 });
或者可以在 theme 中全域開啟它
const theme = createTheme({
components: {
MuiUseMediaQuery: {
defaultProps: {
noSsr: true,
},
},
},
});
伺服器端渲染
首先嘗試依賴客戶端 CSS media query。例如,您可以使用
如果以上替代方案都不是選項,您可以繼續閱讀本文件的此章節。
首先,您需要從伺服器端猜測客戶端請求的特性。您可以選擇使用
- User agent。解析客戶端的 user agent 字串以提取資訊。建議使用 ua-parser-js 來解析 user agent。
- Client hints。讀取客戶端發送到伺服器的 hints。請注意,此功能並非在所有地方都受到支援。
最後,您需要使用先前猜測的特性為 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):一個字串,表示要處理的 media query,或是一個回呼函式,接受 theme(在上下文中)並回傳一個字串。options
(object [optional])
options.defaultMatches
(bool [optional]):由於伺服器上無法使用window.matchMedia()
,因此它會在第一次掛載期間回傳預設匹配項。預設值為false
。options.matchMedia
(func [optional]):您可以提供自己的 matchMedia 實作。這可以用於處理 iframe 內容視窗。options.noSsr
(bool [optional]):預設為false
。為了執行伺服器端 hydration,Hook 需要渲染兩次。第一次使用defaultMatches
,伺服器的值,第二次使用解析後的值。這種雙重渲染循環有一個缺點:速度較慢。如果您僅在客戶端使用回傳值,則可以將此選項設定為true
。options.ssrMatchMedia
(func [optional]):您可以提供自己的 matchMedia 實作,它在伺服器端渲染時使用。
注意:您可以使用 theme 的 default props
功能,透過 MuiUseMediaQuery
鍵來更改預設選項。
回傳值
matches
:如果文件目前符合 media query,則 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>;
}