Как создать React-хук useWindowSize()

Для любого приложения React важно скрывать и отображать элементы нашего приложения в соответствии с шириной и высотой используемого браузера. В этом посте мы расскажем, как это сделать с помощью пользовательского хука React, useWindowSize().

На моем сайте Gatsby у меня есть заголовок, и по мере уменьшения размера страницы я хочу показывать меньше ссылок.

Для этого мы могли бы использовать медиа-запрос (CSS) или мы могли бы использовать настраиваемый хук реагирования, чтобы получить текущий размер страницы и скрыть или показать ссылки в нашем JSX.

Раньше я использовал хук из библиотеки под названием react-use. Вместо того, чтобы приносить целую стороннюю библиотеку, я решил создать свой собственный хук, который предоставлял бы размеры окна, как ширину, так и высоту. Я назвал этот хук useWindowSize.

Создание крючка

Во-первых, мы создадим новый файл .js в папке наших утилит (utils) с тем же именем, что и у хука useWindowSize, и я импортирую React (для использования хуков) при экспорте пользовательского хука.

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {}

Теперь, поскольку я использую это на сайте Gatsby, который обрабатывается сервером, мне нужно получить размер окна, но у нас может не быть доступа к нему, потому что мы находимся на сервере. Чтобы проверить и убедиться, что мы не на сервере, мы можем посмотреть, не равен ли тип window строке undefined.

В этом случае мы можем вернуться к ширине и высоте по умолчанию для браузера, скажем, 1200 и 800 внутри объекта:

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {
  if (typeof window !== "undefined") {
    return { width: 1200, height: 800 };
  }
}

Получение ширины и высоты из окна

И предполагая, что мы находимся на клиенте и можем получить окно, мы можем использовать хук useEffect, чтобы выполнить побочный эффект, взаимодействуя с window. Мы включим пустой массив зависимостей, чтобы убедиться, что функция эффекта вызывается только после монтирования компонента (в котором вызывается этот хук).

Чтобы узнать ширину и высоту окна, мы можем добавить прослушиватель событий и прослушивать событие resize. И всякий раз, когда размеры браузера изменяются, мы можем обновить часть состояния (созданную с помощью useState), которую мы назовем windowSize, а сеттер для ее обновления будет setWindowSize.

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {
  if (typeof window !== "undefined") {
    return { width: 1200, height: 800 };
  }

  const [windowSize, setWindowSize] = React.useState();

  React.useEffect(() => {
    window.addEventListener("resize", () => {
      setWindowSize({ width: window.innerWidth, height: window.innerHeight });
    });
  }, []);
}

При изменении размера окна будет вызван обратный вызов, и состояние windowSize будет обновлено текущими размерами окна. Чтобы получить это, мы устанавливаем ширину window.innerWidth и высоту window.innerHeight.

Добавление поддержки SSR

Однако код в том виде, в котором он здесь представлен, работать не будет. И причина в том, что ключевое правило хуков заключается в том, что их нельзя вызывать условно. В результате у нас не может быть условного оператора над нашим хуком useState или useEffect до того, как они будут вызваны.

Поэтому, чтобы исправить это, мы установим начальное значение useState условно. Мы создадим переменную с именем isSSR, которая будет выполнять ту же проверку, чтобы убедиться, что окно не равно строке undefined.

И мы будем использовать троицу, чтобы установить ширину и высоту, сначала проверив, находимся ли мы на сервере. Если мы будем использовать значение по умолчанию, а если нет, мы будем использовать window.innerWidth и window.innerHeight.

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {
  // if (typeof window !== "undefined") {
  //  return { width: 1200, height: 800 };
  // }
  const isSSR = typeof window !== "undefined";
  const [windowSize, setWindowSize] = React.useState({
    width: isSSR ? 1200 : window.innerWidth,
    height: isSSR ? 800 : window.innerHeight,
  });

  React.useEffect(() => {
    window.addEventListener("resize", () => {
      setWindowSize({ width: window.innerWidth, height: window.innerHeight });
    });
  }, []);
}

Затем, наконец, нам нужно подумать о том, когда наши компоненты размонтируются. Что нам нужно сделать? Нам нужно удалить наш прослушиватель изменения размера.

Удаление прослушивателя события изменения размера

Вы можете сделать это, вернув функцию из useEffect, и мы удалим слушателя с помощью window.removeEventListener.

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {
  // if (typeof window !== "undefined") {
  //  return { width: 1200, height: 800 };
  // }
  const isSSR = typeof window !== "undefined";
  const [windowSize, setWindowSize] = React.useState({
    width: isSSR ? 1200 : window.innerWidth,
    height: isSSR ? 800 : window.innerHeight,
  });

  React.useEffect(() => {
    window.addEventListener("resize", () => {
      setWindowSize({ width: window.innerWidth, height: window.innerHeight });
    });

    return () => {
      window.removeEventListener("resize", () => {
        setWindowSize({ width: window.innerWidth, height: window.innerHeight });
      });
    };
  }, []);
}

Но так как нам нужна ссылка на одну и ту же функцию, а не на две разные, как здесь. Для этого мы создадим общую функцию обратного вызова для обоих слушателей с именем changeWindowSize.

И, наконец, в конце хука мы вернем наше состояние windowSize. И это все.

// utils/useWindowSize.js

import React from "react";

export default function useWindowSize() {
  const isSSR = typeof window !== "undefined";
  const [windowSize, setWindowSize] = React.useState({
    width: isSSR ? 1200 : window.innerWidth,
    height: isSSR ? 800 : window.innerHeight,
  });

  function changeWindowSize() {
    setWindowSize({ width: window.innerWidth, height: window.innerHeight });
  }

  React.useEffect(() => {
    window.addEventListener("resize", changeWindowSize);

    return () => {
      window.removeEventListener("resize", changeWindowSize);
    };
  }, []);

  return windowSize;
}

использование

Чтобы использовать хук, нам просто нужно импортировать его туда, где нам нужно, вызвать его и использовать ширину везде, где мы хотим скрыть или показать определенные элементы.

В моем случае это отметка 500px. Там я хочу скрыть все остальные ссылки и показать только кнопку «Присоединиться сейчас», как вы видите в примере выше:

// components/StickyHeader.js

import React from "react";
import useWindowSize from "../utils/useWindowSize";

function StickyHeader() {
  const { width } = useWindowSize();

  return (
    <div>
      {/* visible only when window greater than 500px */}
      {width > 500 && (
        <>
          <div onClick={onTestimonialsClick} role="button">
            <span>Testimonials</span>
          </div>
          <div onClick={onPriceClick} role="button">
            <span>Price</span>
          </div>
          <div>
            <span onClick={onQuestionClick} role="button">
              Question?
            </span>
          </div>
        </>
      )}
      {/* visible at any window size */}
      <div>
        <span className="primary-button" onClick={onPriceClick} role="button">
          Join Now
        </span>
      </div>
    </div>
  );
}

Этот хук будет работать с любым серверным приложением React, таким как Gatsby и Next.js.

Хотите стать мастером JS? Присоединяйтесь к JS Bootcamp 2020 🏕️

Подпишись + Скажи привет! 🎨 ТвиттерИнстаграмreedbarger.comcodeartistry.io