Как использовать хуки React в Gatsby

В JavaScript функции являются наиболее влиятельными элементами кода. Особенно это стало заметно в React с появлением хуков в версии 16.8. Они позволяют управлять состоянием и внедрять побочные эффекты (side effects) в функциональные компоненты.

Gatsby использует простой React со всеми его функциями. Это означает, что в Gatsby доступны хуки, их можно использовать с оператором import. Давайте посмотрим, как мы можем применить хуки в Gatsby.

Обновление React

Чтобы использовать хуки в Gatsby, ничего устанавливать не надо. Необходимо только иметь последнюю версию React и Gatsby (ну или хотя бы версию 16.8+). Для начала проверьте файл package.json, чтобы определить, какую версию вы установили.

Читайте также: Анатомия файла package.json

Если вам нужно обновить свои пакеты, запустите следующие команды:

$ npm install react@16.8.0 react-dom@16.8.0

# или

$ yarn add react@16.8.0 react-dom@16.8.0

Использование хуков

Теперь давайте настроим компонент header.js с раскрывающимся меню.

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

В файл src/components/header.js поместите:

import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';

const Header = () => {
  // определяет, прокручена ли страница и просматривается ли она на мобильном устройстве
  const [scrolled, setScrolled] = useState(false);

  // меняет состояние при прокрутке
  useEffect(() => {
    const handleScroll = () => {
      const isScrolled = window.scrollY > 10;
      if (isScrolled !== scrolled) {
        setScrolled(!scrolled);
      }
    };

    document.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
      // очистит обработчик событий, когда компонент отключается
      document.removeEventListener('scroll', handleScroll);
    };
  }, [scrolled]);

  return (
    <header data-active={scrolled}>
      <Link to="/">React Hooks on Gatsby</Link>
      <nav>
        <Link to="/about/">About</Link>
        <Link to="/contact/">Contact Us</Link>
      </nav>
    </header>
  );
};

export default Header;

Свойство window.scrollY возвращает количество пикселей, прокрученных вертикально. Мы сравниваем это значение с 10 пикселями и получаем логическое значение, которое сообщит, переместил пользователь документ или нет. Затем мы оборачиваем условное свойство в функцию, которая обновляет состояние scrolled всякий раз, когда пользователь прокручивает сайт. Затем эта функция передается прослушивателю событий в документе.

Все это будет находиться внутри хука useEffect, который вернет removeEventListener в документе, чтобы очистить обработчик событий при отключении компонента. Хук useEffect позволяет нам выполнять побочные эффекты для нашего компонента. По умолчанию эффект срабатывает после каждого завершенного рендеринга, однако мы можем передать второй аргумент – массив значений, от которых зависит срабатывание эффекта (в нашем случае это [scrolled]).

Таким образом, мы можем добавить в наш HTML-код идентифицирующий атрибут для определения состояния элемента. Мы будем использовать атрибут data-active с логическим значением из состояния scrolled. А в CSS с помощью селектора атрибутов можно добавить эффект тени box-shadow. Для этого откройте файл src/styles/main.scss и вставьте в него:

header {
  position: fixed;
  top: 0;
  transition: box-shadow .3s ease;
  width: 100%;

  &[data-active='true'] {
    box-shadow: 0 2px 8px rgba(152,168,188,.2);
  }
}

Тот же стиль можно использовать и со стилизованными компонентами. Замена селектора заголовка на теговый шаблон компонента обеспечит ту же функциональность.

Хуки и пользовательский ввод

Давайте немного изменим этот пример и добавим раскрывающееся меню. Допустим, доступ к нему можно получить с помощью кнопки-переключателя. Мы можем сохранить большую часть того кода, что написали ранее, и просто изменить свойства изменения состояния. М переименуем свойство в state и setState при получении объекта с переменными состояния.

В этом случае обновление состояния будет немного происходить немного иначе. Во-первых, нужно передать предыдущее состояние в качестве spread-оператора, после которого указать обновленное значение: отличие от компонентов класса, функциональные компоненты будут заменять обновленные объекты, а не объединять их. Отредактируйте файл src/components/header.js:

import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';

import Dropdown from './dropdownMenu';

const Header = () => {
  // определяет, прокручена ли страница и просматривается ли она на мобильном устройстве
  const [state, setState] = useState({
    scrolled: false,
    visible: false,
  });

  // изменяет состояние при прокрутке
  useEffect(() => {
    const handleScroll = () => {
      const isScrolled = window.scrollY > 10;
      if (isScrolled !== state.scrolled) {
        setState({
          ...state,
          scrolled: !state.scrolled,
        });
      }
    };
    document.addEventListener('scroll', handleScroll, { passive: true });
    return () => {
      // очищает обработчик событий, когда компонент отключается
      document.removeEventListener('scroll', handleScroll);
    };
  }, [state.scrolled]);

  // toggle dropdown visibility
  const toggleVisibility = () => {
    setState({
      ...state,
      visible: !state.visible,
    });
  };

  return (
    <header data-active={state.scrolled}>
      <Link to="/">React Hooks on Gatsby</Link>
      <nav>
        <Link to="/about/">About</Link>
        <Link to="/contact/">Contact Us</Link>
        <button onClick={toggleVisibility} type="button">
          Solutions
        </button>
        <Dropdown
          aria-hidden={!state.visible}
          data-active={state.visible}
        />
      </nav>
    </header>
  );
};

export default Header;

Итак, мы хотим, чтобы пользователь нажал кнопку, которая откроет дополнительное меню. При нажатии кнопки Solutions происходит переключение логического значения visible. Это значение передается в атрибуты aria-hidden и data-active для использования в CSS.

// элемент section представляет наш компонент <Dropdown />

header {
  top: 0;
  transition: box-shadow .3s ease;

  &[data-active='true'] {
    box-shadow: 0 2px 8px rgba(152,168,188,.2);
  }

  &,
  section {
    position: fixed;
    width: 100%;
  }

  nav,
  section {
    overflow: hidden;
  }

  section {
    height: 0;
    left: 0;
    opacity: 0;
    right: 0;
    top: 5.5rem;
    transition: all .3s ease-in-out;
    visibility: hidden;

    &[data-active='true'] {
      height: auto;
      opacity: 1;
      visibility: visible;
    }
  }
}

Заключение

С помощью хуков мы получаем все преимущества компонентов класса внутри функций, и Gatsby пользуется этим в полной мере. Рекомендуем вам ознакомиться со всеми хуками, доступными в документации React. Вы даже можете попробовать создать собственные хуки.

Tags: ,

Добавить комментарий