跳到主要內容
+

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

(min-width:600px) matches: false
按下 Enter 開始編輯

⚠️ 由於瀏覽器的限制,您不能使用 '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.breakpoints.up('sm') matches: false

或者,您可以使用回呼函式,將 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 字串。

{ minWidth: 600 } matches: false
按下 Enter 開始編輯

測試

您需要在您的測試環境中實作 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>,
  );

  // …
}
(min-width:600px) matches: true

請確保您為客戶端提供相同的自訂 matchMedia 實作,以保證 hydration 匹配。

從 withWidth() 遷移

withWidth() 高階元件注入頁面的螢幕寬度。您可以使用 useWidth Hook 重現相同的行為

width: xs
按下 Enter 開始編輯

API

useMediaQuery(query, [options]) => matches

參數

  1. query (string | func):一個字串,表示要處理的 media query,或是一個回呼函式,接受 theme(在上下文中)並回傳一個字串。
  2. 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,則 matchestrue,否則為 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>;
}