React паттерн headless component

месяц назад·2 мин. на чтение

Рассмотрим использование паттерна headless component для создания модульных и гибких компонентов в React приложениях.

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

Как это работает?

Суть этого шаблона заключается в том, чтобы отделить бизнес-логику от UI части самого компонента. Когда в будущем потребуется обновить пользовательский интерфейс, это можно сделать, не затрагивая код бизнес-логики. Паттерн, который мы собираемся использовать, называется headless component. В этом шаблоне компонент реализован в виде хуков React. Этот компонент отвечает за логику и управление состоянием. Headless-компонент ничего не знает о пользовательском интерфейсе. Создается отдельный компонент, который отвечает только за пользовательский интерфейс. Паттерн используется многими популярными библиотеками компонентов React, такими как: DownShift, React Table и т.д.

Ваш первый headless компонент

Начнем с написания первого React компонента с использованием этого шаблона. Ниже приведен пример компонента dropdown:
const useDropdown = (items: Item[]) => {
  // состояние
  const [selectedIndex, setSelectedIndex] = useState<number>(-1);
  const [isOpen, setIsOpen] = useState(false);
  const [selectedItem, setSelectedItem] = useState<Item | null>(null);

  // функция возвращающая aria-атрибуты для UI
  const getAriaAttributes = () => ({
    role: "combobox",
    "aria-expanded": isOpen,
    "aria-activedescendant": selectedItem ? selectedItem.text : undefined,
  });

  const handleKeyDown = (e: React.KeyboardEvent) => {
    // реализация на основе switch-case
  };
  
  const toggleDropdown = () => setIsOpen((isOpen) => !isOpen);

  return {
    isOpen,
    toggleDropdown,
    handleKeyDown,
    selectedItem,
    setSelectedItem,
    selectedIndex,
  };
};
Давайте интегрируем наш headless-компонент с компонентом, отвечающим за обработку пользовательского интерфейса:
const Dropdown = ({ items }: DropdownProps) => {
  const {
    isOpen,
    selectedItem,
    selectedIndex,
    toggleDropdown,
    handleKeyDown,
    setSelectedItem,
  } = useDropdown(items);

  return (
    <div className="dropdown" onKeyDown={handleKeyDown}>
      <Trigger
        onClick={toggleDropdown}
        label={selectedItem ? selectedItem.text : "Select an item..."}
      />
      {isOpen && (
        <DropdownMenu
          items={items}
          onItemClick={setSelectedItem}
          selectedIndex={selectedIndex}
        />
      )}
    </div>
  );
};
Выше приведен очень простой пример компонента. Представьте себе возможность, когда один и тот же шаблон интегрируется с более сложными приложениями. Шаблон headless component является чрезвычайно мощным, когда речь идет о создании перспективных компонентов с высокой степенью расширяемости.

Когда использовать этот шаблон?

Преимущество этого шаблона раскрывается при создании большого приложения, которое часто меняется. Реализация этого шаблона не только превращает компоненты React в расширяемые, но и делает их очень удобными в сопровождении в большой кодовой базе.

Асинхронные запросы и обновление состояния в React

месяц назад·1 мин. на чтение

В этой статье мы рассмотрим, как выполнять асинхронные запросы к серверу и обновлять состояние компонентов в React.

Одной из ключевых особенностей React является его способность эффективно обрабатывать асинхронные запросы и обновлять состояние компонентов. В этой статье мы рассмотрим, как использовать эти возможности в функциональных компонентах. Функциональные компоненты в React - это простые функции, которые принимают набор свойств (props) и возвращают элементы React. Они являются предпочтительным способом создания компонентов в современной версии React. Один из наиболее распространенных сценариев использования асинхронных запросов в React - это получение данных из API. Для этого можно использовать функцию fetch(), которая возвращает объект Promise. Promise - это механизм JavaScript, который позволяет выполнять асинхронные операции и обрабатывать их результаты. В функциональном компоненте вы можете использовать хук useState для создания состояния. Состояние - это данные, которые могут изменяться в процессе выполнения приложения. Давайте создадим простой компонент, который получает данные из API и обновляет состояние:
import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => console.error(error));
  }, []);

  if (data === null) {
    return <div>Loading...</div>;
  }

  return <div>{data}</div>;
}

export default MyComponent;
В этом примере мы использовали хук useEffect, чтобы выполнить асинхронный запрос при каждом рендеринге компонента. Параметр [] во втором аргументе useEffect указывает, что эффект должен быть выполнен только один раз при монтировании компонента. Когда данные будут получены, мы обновляем состояние с помощью функции setData. Обновление состояния вызывает повторный рендеринг компонента, и теперь мы можем отобразить полученные данные. Также можно использовать хук useEffect для выполнения других действий после обновления состояния. Например, вы можете отправить данные на сервер после их обновления:
useEffect(() => {
  if (data !== null) {
    fetch('https://api.example.com/update', {
      method: 'POST',
      body: JSON.stringify(data)
    })
      .then(response => response.json())
      .then(result => console.log(result))
      .catch(error => console.error(error));
  }
}, [data]);
В этом примере мы используем параметр [data] во втором аргументе useEffect, чтобы эффект был выполнен только при изменении состояния data. Таким образом, мы отправляем данные на сервер только тогда, когда они изменяются. Теперь вы знаете, как использовать асинхронные запросы и обновление состояния в функциональных компонентах React. Это мощный инструмент для создания динамических и интерактивных пользовательских интерфейсов. Не стесняйтесь экспериментировать и улучшать свои навыки разработки с помощью React!