useMediaQuery
Este hook de React busca coincidencias con una consulta de medios CSS. Permite la representación de componentes en función de si la consulta coincide o no.
Algunas de las características clave:
- ⚛️ Tiene una API React idiomática.
- 🚀 Es performant, observa el documento para detectar cuando cambian sus media queries, en lugar de sondear los valores periódicamente.
- 🤖 Soporta renderizado del lado del servidor.
Media query básica
Debes proporcionar una media query al primer argumento del hook.
La cadena media query puede ser cualquier media query CSS válida, por ejemplo '(prefers-color-scheme: dark)'
.
⚠️ No puede utilizar 'print'
por limitación del navegador, por ejemplo Firefox.
Uso de los helpers de breakpoints
Puedes utilizar los breakpoint helpers de U-Ui de la siguiente manera:
import { useTheme } from '@u_ui/u-ui/styles';
import useMediaQuery from '@u_ui/u-ui/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up('sm'));
return <span>{`theme.breakpoints.up('sm') matches: ${matches}`}</span>;
}
Alternativamente, puede utilizar una función de devolución de llamada, aceptando el tema como primer argumento:
import useMediaQuery from '@u_ui/u-ui/useMediaQuery';
function MiComponent() {
const matches = useMediaQuery((theme) => theme.breakpoints.up('sm'));
return <span>{`theme.breakpoints.up('sm') matches: ${matches}`}</span>;
}
⚠️ No hay soporte de temas por defecto, tienes que inyectarlo en un proveedor de temas padre.
Uso de la sintaxis JavaScript
Puede utilizar json2mq para generar cadenas de consulta de medios a partir de un objeto JavaScript.
Pruebas
Necesita una implementación de matchMedia en su entorno de pruebas.
Por ejemplo, jsdom no lo soporta todavía (Inglés). Deberías hacer un polyfill. Se recomienda utilizar css-mediaquery para emularlo.
import mediaQuery from 'css-mediaquery';
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addEventListener: () => {},
removeEventListener: () => {},
});
}
describe('MisPruebas', () => {
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
});
Renderizado sólo del lado del cliente
Para realizar la hidratación del lado del servidor, el hook necesita renderizar dos veces.
Una primera vez con defaultMatches
, el valor del servidor, y una segunda vez con el valor resuelto.
Este ciclo de renderizado de doble pasada tiene un inconveniente: es más lento.
Puede establecer la opción noSsr
a true
si utiliza el valor devuelto sólo del lado del cliente.
const matches = useMediaQuery('(min-width:600px)', { noSsr: true });
o puede activarlo globalmente con el tema:
const theme = createTheme({
components: {
uiUseMediaQuery: {
defaultProps: {
noSsr: true,
},
},
},
});
Renderizado del lado del servidor
Intente utilizar primero las consultas de medios CSS del lado del cliente. Por ejemplo, puede utilizar:
Si ninguna de las alternativas anteriores es una opción, puede continuar leyendo esta sección de la documentación.
Primero, necesitas adivinar las características de la petición del cliente, desde el servidor. Puede elegir entre utilizar
- Agente de usuario. Analiza la cadena de agente de usuario del cliente para extraer información. Se recomienda utilizar ua-parser-js para analizar el agente de usuario.
- Pistas del cliente. Lee las sugerencias que el cliente envía al servidor. Tenga en cuenta que esta función no está soportada en todas partes (Inglés).
Por último, necesita proporcionar una implementación de matchMedia (Inglés) a la useMediaQuery
con las características adivinadas anteriormente.
Se recomienda utilizar css-mediaquery para emular matchMedia.
Por ejemplo, en el lado del servidor:
import * as ReactDOMServer from 'react-dom/server';
import parser from 'ua-parser-js';
import mediaQuery from 'css-mediaquery';
import { createTheme, ThemeProvider } from '@u_ui/u-ui/styles';
function handleRender(req, res) {
const deviceType = parser(req.headers['user-agent']).device.type || 'desktop';
const ssrMatchMedia = (query) => ({
matches: mediaQuery.match(query, {
// La anchura CSS estimada del navegador.
width: deviceType === 'mobile' ? '0px' : '1024px',
}),
});
const theme = createTheme({
components: {
// Cambiar las opciones por defecto de useMediaQuery
uiUseMediaQuery: {
defaultProps: {
ssrMatchMedia,
},
},
},
});
const html = ReactDOMServer.renderToString(
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>,
);
// …
}
Asegúrese de proporcionar la misma implementación de medios de coincidencia personalizados al lado del cliente para garantizar una coincidencia de hidratación.
Migración desde withWidth()
El componente de orden superior withWidth()
inyecta el ancho de pantalla de la página.
Puedes reproducir el mismo comportamiento con un hook useWidth
:
API
useMediaQuery(query, [options]) => matches
Argumentos
query
(string | func): Una cadena que representa la consulta de medios a manejar o una función de devolución de llamada que acepta el tema (en el contexto) que devuelve una cadena.options
(object [optional]):
options.defaultMatches
(bool [opcional]): Comowindow.matchMedia()
no está disponible en el servidor, devuelve una coincidencia por defecto durante el primer montaje. El valor por defecto esfalse
.options.matchMedia
(func [opcional]): Puede proporcionar su propia implementación de matchMedia. Puede utilizarse para manejar una ventana de contenido iframe.options.noSsr
(bool [opcional]): Por defecto esfalse
. Para realizar la hidratación del lado del servidor, el hook necesita renderizarse dos veces. Una primera vez condefaultMatches
, el valor del servidor, y una segunda vez con el valor resuelto. Este ciclo de renderizado de doble pasada tiene un inconveniente: es más lento. Puede establecer esta opción atrue
si utiliza el valor devuelto sólo del lado del cliente.options.ssrMatchMedia
(func [opcional]): Puedes proporcionar tu propia implementación de matchMedia, se usa cuando se renderiza del lado del servidor.
Nota: Puedes cambiar las opciones por defecto usando la función default props
del tema con la clave uiUseMediaQuery
.
Devuelve
coincidencias: Matches es true
si el documento coincide con la media query y false
si no coincide.
Ejemplos
import * as React from 'react';
import useMediaQuery from '@u_ui/u-ui/useMediaQuery';
export default function SimpleMediaQuery() {
const matches = useMediaQuery('(min-width:600px)');
return <span>{`(min-width:600px) matches: ${matches}`}</span>;
}