Друкарня від WE.UA

React-хук для роботи з matchMedia

Готовий кастомний хук. Достатньо скопіювати — і матимете зручну можливість отримувати дані від matchMedia, задавати кастомні медіа-запити, легко їх змінювати та використовувати все це у React.

У попередній статті про різноманітні підходи до роботи над адаптивністю веб-сайтів я згадувала matchMedia — JavaScript API, який дозволяє отримувати відповіді true/false на будь-який ваш медіа-запит та керувати кодом в залежності від розміру екрану. Це напрочуд крутий інструмент в разі якщо у вашому проекті багато елементів, що мають реагувати на зміни екрану у корстувача. Проте його не можна використовувати за просто так у React. Як спеціаістка, що найбільше працює саме з рекатом, я написала кастомний хук, яким ділюся з вами.

Повний код хука на CodePen

А тепер по порядку

В статті я розказую про:

  1. npm-пакет для роботи з matchMedia, який вже існує

  2. плюси свого хука

  3. що відбувається під капотом


На npm можна знайти пакет react-use-match-media для роботи з matchMedia в React і він більш ніж корисний, якщо у вас в проекті необхідність звертатись до медіа-запитів саме через js сягає 1-3 разів на весь код. Тоді не треба винаходити велосипеда, просто беріть цей пакет. Приблизно так його використовують:

const Example = (props) => {
  const isWideViewport = useMatchMedia('(min-width: 600px)');

  return <div>{isWideViewport ? 'Wide' : 'Narrow'}</div>;
};

Незручніть полягає в тому, що хук стає важким у застосуванні в разі, коли ваш проект потребує багато різних медіа-запитів в різних місцях коду. По-перше вам необхідно кожного разу вручну прописуати конкретний запит — як у прикладі '(min-width: 600px)'. В разі, якщо параметри на всьому проекті зміняться — доведеться шукати по проекту усі випадки його застосування, або знову ж таки зазделегідь обгортати цей пакет в додатковий код. До того ж все це виглядає не сказати, що інтуітивно зрозуміло, тому нові люди на проекті витратять більше часу, щоб розібратись з адаптивністю.

Що натомість пропоную я:

В кастомний хук ви передаєте не рядок, а свій об’єкт, куди прописуєте одразу декілька медіа-запитів з інтуітино зрозумілими назвами, наприклад ось такий:

const myMediaQueries = {
    mobile: '(max-width: 480px)',
    tablet: '(min-width: 481px) and (max-width: 767px)',
    desktop: '(min-width: 768px)',
}

І хук повертає вам також готовий об`єкт з відповідними ключами та значеннями true/false, який можна одразу зручно розпарсити:

// [object Object] 
{
  "mobile": true,
  "tablet": false,
  "desktop": false
}
export const Header = () => {

  const { mobile, tablet, desktop } = reactMatchMedia(myMediaQueries);

  return mobile ? <div>Hello, Mobile World<div> : <div>Hello, World</div>

};

Ба більше (!), якщо вам знадобиться прибрати чи додати один з параметрів, достатньо це зробити в початковій об’єкті-константі myMediaQueries. На прикладі нижче ми додаємо в наш проект великі екрани:

const myMediaQueries = {
    mobile: '(max-width: 480px)',
    tablet: '(min-width: 481px) and (max-width: 767px)',
    desktop: '(min-width: 768px) and (max-width: 1023px)',
    bigScreen: '(min-width: 1024px)',
}

Хук тепер повертає також і новий ключ, про що можна переконатись у консолі:

// [object Object] 
{
  "mobile": false,
  "tablet": false,
  "desktop": false,
  "bigScreen": true
}

Також без вагань ви можете додати в свій об’єкт запити не тільки розімірів, а й орієнтацій, наприклад:

const myMediaQueries = {
    mobile: '(max-width: 480px)',
    tablet: '(min-width: 481px) and (max-width: 767px)',
    desktop: '(min-width: 768px) and (max-width: 1023px)',
    landscape: '(orientation: landscape)',
}

Тепер, розпарсивши результат, легше проводити більш комплексні логічні операції:

export const Header = () => {

  const { mobile, landscape } = reactMatchMedia(myMediaQueries);
  if (mobile && landscape) {
    return (
      <div> 
        *для горизонтальної орієнтації*
      <div>
)};

Таким чином, завдяки цьому хуку, можна брати потрібні значення, які мають зрозумілі назви, парсити їх на початку компонента і далі з легкістю вживати де завгодно.

Під капотом

// Хук
const reactMatchMedia = (mediaQueries) => {
  
  const queries = Object.values(mediaQueries).map((query) => matchMedia(query));

  const [values, setValues] = useState(() => {
    const valuesObject = Object.keys(mediaQueries).reduce((acc, key, index) => {
      acc[key] = queries[index].matches;
      return acc;
    }, {});

    // Милиця! Для браузрених багів
    if (Object.values(valuesObject).every((val) => val === false)) {
      valuesObject.mobile = true;
    }

    return valuesObject;
  });

  useLayoutEffect(() => {
    const updateValues = () => {
      const valuesObject = Object.keys(mediaQueries).reduce(
        (acc, key, index) => {
          acc[key] = queries[index].matches;
          return acc;
        },
        {}
      );

      // Милиця! Для браузрених багів
      if (Object.values(valuesObject).every((val) => val === false)) {
        valuesObject.mobile = true;
      }

      setValues(valuesObject);
    };

    queries.forEach((query) => query.addEventListener("change", updateValues));
    return () =>
      queries.forEach((query) =>
        query.removeEventListener("change", updateValues)
      );
  }, [queries]);

  return values;
};

Власне, що тут відбувається?

Хук отримує кастомний об’єкт, використовує редуктор для отримання від matchMedia значень .matches для кожного ключа, щоб записати їх у початковий стан. Я використовую власне useState для створення стану та useLayoutEffect — для реагування на зміни.

Чому useLayoutEffect, а не звичайний useEffect? Звичайний useEffect виконується після того, як браузер оновить екран, іншими словами, після рендерингу компонентів. Це може спричинити миготіння або затримку в зміні стану компонентів, якщо значення медіа-запитів впливають на їх розміщення або вигляд.

Використання useLayoutEffect гарантує, що оновлення значень медіа-запитів буде відбуватись безпосередньо перед малюванням на екрані, що нам власне і потрібно.

У хуці є милиця. Вона створена для браузенрних багів при роботі з matchMedia. Це рідкість, але нажаль трапляється, або може трапитись в майбутньому після оновлень браузрерів. Деякі браузери в деяких випадках повертають false для всіх медіа-запитів, навіть якщо насправді одне зі значень має бути true. Для цього я в милиці встановлюю значення mobile як true примусово, щоб не зламати подальший код. Дефолтне значення може бути встановлено будь яке — най це буде desktop, якщо ваші користувачі ймовірніше заходять зі стаціонарного пристрою.

Цей хук не є легеньким, хоча б тому, що useLayoutEffect синхроний. Проте npm-пакет react-use-match-media так само під капотом використовує саме його. Мій кастомний хук важчий за react-use-match-media хіба через використання ще й редуктора, але він і приймає більш комплексні дані, а не один єдиний рядок.


Зберігайте собі, одного дня може згадаєте мене добрим словом! А я постараюсь писати тут частіше, хоча це не дуже й просто влітку, коли собаки просять гуляти з ними, а не сидіти за компом:)

Статті про вітчизняний бізнес та цікавих людей:

  • Вітаємо з Різдвом Христовим!

    Друкарня та платформа WE.UA вітають всіх наших читачів та авторів зі світлим святом Різдва! Зичимо всім українцям довгожданого миру, міцного здоровʼя, злагоди, родинного затишку та втілення всього доброго і прекрасного, чого вам побажали колядники!

    Теми цього довгочиту:

    Різдво
  • Каблучки – прикраси, які варто купувати

    Ювелірні вироби – це не тільки спосіб витратити гроші, але і зробити вигідні інвестиції. Бо вартість ювелірних виробів з кожним роком тільки зростає. Тому купуючи стильні прикраси, ви вигідно вкладаєте кошти.

    Теми цього довгочиту:

    Як Вибрати Каблучку
  • П'ять помилок у виборі домашнього текстилю, які псують комфорт сну

    Навіть ідеальний матрац не компенсує дискомфорт, якщо текстиль підібрано неправильно. Постільна білизна безпосередньо впливає на терморегуляцію, стан шкіри та глибину сну. Більшість проблем виникає не через низьку якість виробів, а через вибір матеріалів та подальшу експлуатацію

    Теми цього довгочиту:

    Домашній Текстиль
  • Як знайти житло в Києві

    Переїжджаєте до Києва і шукаєте житло? Дізнайтеся, як орендувати чи купити квартиру, перевірити власника та знайти варіанти, про які зазвичай не говорять.

    Теми цього довгочиту:

    Агентство Нерухомості
  • Як заохотити дитину до читання?

    Як залучити до читання сучасну молодь - поради та факти. Користь читання для дітей - основні переваги. Розвиток дітей - це наше майбутнє.

    Теми цього довгочиту:

    Читання
Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Марта Ярчук
Марта Ярчук@yarchuk

331Прочитань
38Автори
39Читачі
На Друкарні з 18 квітня

Більше від автора

Це також може зацікавити:

  • Як підвищити ефективність лендингів за допомогою JavaScript-скриптів

    Сучасні лендинги потребують постійного оновлення, щоб залишатися ефективними. Динамічні елементи, валідація номерів, актуальні дати та захист від спаму допомагають збільшити конверсію та знизити витрати на рекламу.

    Теми цього довгочиту:

    Ефективність Лендингів
  • [Frontend] Універсальні рішення

    Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення".

    Теми цього довгочиту:

    It
  • λanguage: Неформальний опис мови

    Автор пояснює, як написати парсер, інтерпретатор, компілятор, та інші складові власної мови програмування. Описується синтаксис мови, особливості реалізації функцій, умовних конструкцій та інших елементів мови.

    Теми цього довгочиту:

    Js

Коментарі (2)

Цікавий матеріал, але поправте назву хука, щоб по конвенціях була - починалася з use, будь ласка

Це також може зацікавити:

  • Як підвищити ефективність лендингів за допомогою JavaScript-скриптів

    Сучасні лендинги потребують постійного оновлення, щоб залишатися ефективними. Динамічні елементи, валідація номерів, актуальні дати та захист від спаму допомагають збільшити конверсію та знизити витрати на рекламу.

    Теми цього довгочиту:

    Ефективність Лендингів
  • [Frontend] Універсальні рішення

    Потряпляючи у пастки антипатернів велику кількість разів ви навчитесь писати гарні “універсальні рішення".

    Теми цього довгочиту:

    It
  • λanguage: Неформальний опис мови

    Автор пояснює, як написати парсер, інтерпретатор, компілятор, та інші складові власної мови програмування. Описується синтаксис мови, особливості реалізації функцій, умовних конструкцій та інших елементів мови.

    Теми цього довгочиту:

    Js