Реализация масштабируемого React-приложения с современными подходами взаимодействия с внешними API

В статье предлагается подход к разработке клиентской части web-приложения с эффективным взаимодействием с сервером в декларативном стиле программирования. Данный подход помогает быстро реализовать масштабируемое веб приложение с удобным менеджментом запросов и кэшированием полученных данных.

Аннотация статьи
front-end
web-приложение
react
взаимодействие с сервером
масштабируемое приложение
Ключевые слова

1. Введение

Наиболее популярный подход в Web разработке приложений сейчас это разделение его на front-end и back-end части. Front-end часть – это пользовательский интерфейс, который может быть открыт в web-браузере пользователя. Back-end часть – это выполняемая на стороне сервера часть приложения, которая предоставляет API для взаимодействия (Акроним от Application Programming Interface).

В данной статье я рассмотрю способ разработки Front-end части веб приложения, способной получать данные с открытого API и выводить их для пользователя, а также взаимодействовать. Показанная концепция подойдет как для построения небольших приложений, так и для масштабных. В основе примера будут применены: библиотека для построения пользовательских интерфейсов React, а также библиотеки Axios и React Query.

Упор делается на простоту разработки подобного решения, а также удобство в масштабировании.

Разработанное в ходе статьи приложение будет отображать данные полученные из открытого API, отображать их и при необходимости обновлять их. Не стоит цели демонстрации разработки Back-end части.

2. Создание нового одностраничного web-приложения

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

Одним из основных требований данного подхода является Node.js, который дает ряд возможностей и является неотъемлемой части современной front-end разработки. Он является программной платформой, превращающей JavaScript из узкоспециализированного языка в язык общего назначения. Используемый в данной статье сборщик приложения, работает на его основе, а также возможности экспорта и импорта файлов, чтобы создать удобную структуру приложения, а не хранить весь код в одном файле.

Так же я предлагаю использовать npm — менеджер пакетов, входящий в состав Node.js. Менеджер пакетов требуется для удобного управления всеми зависимостями, которые потребуются для реализации приложения.

Установить Node.js можно с официального сайта https://nodejs.org/en/ В процессе разработке я использовал версию 16.13.1.

Команда React подготовила удобную среду для удобного старта разработки React приложения, процесс его использования описан на официальном сайте https://ru.reactjs.org/docs/create-a-new-react-app.html

Необходимо в терминале установить библиотеку в наш проект через пакетный менеджер npm, который будет доступен в терминале после установки Node.js. Для этого перейдя в нем в директорию, в которой вы решите разрабатывать приложение и прописать три команды, где вместо my-app прописать название вашего проекта

  1. npx create-react-app my-app
  2. cd my-app
  3. npm start

Рис. 1. Созданное React приложение

Далее локальный сервер запустится и будет доступен в web браузере, по умолчанию по адресу http://localhost:3000/.

На рисунке 1 показана базовая версия приложения, отображаемая в web-браузере. Как в нем написано, требуется изменить содержимое файла App.js. Данный файл находится в папке src в структуре созданного приложения.

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

function App() {
   return <div />;
}
export default App;

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

3. Установка необходимых зависимостей

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

Для выполнения HTTP запросов к API я предлагаю использовать популярный для этого клиент Axios, который документирован на его официальном сайте https://axios-http.com/docs/intro. Без использования данной библиотеки придется самостоятельно реализовывать XMLHttpRequests, что усложняет разработку и делает код менее читаемым.

Для его установки в терминале из папки проекта требуется выполнить следующую команду:

npm install axios

Данная библиотека позволяет отправлять HTTP запросы из приложения используя простой интерфейс.

Кроме того, по умолчанию приложения React не имеют встроенного способа извлечения или обновления данных из компонентов, поэтому разработчики создают собственные способы извлечения данных. Обычно при функциональных React  компонентах (https://ru.reactjs.org/docs/components-and-props.html) это означает объединение useState и useEffect на основе компонентов с помощью React hooks  (https://ru.reactjs.org/docs/hooks-intro.html) или использование библиотек управления состоянием для хранения и предоставления асинхронных данных в приложениях, что часто является излишним и усложняет дальнейшую поддержку приложения.

Я предлагаю для решения данного вопроса использование React Query  – это библиотека, которая упрощает получение, кэширование, синхронизацию и обновление состояния сервера в приложениях, реализованных на React. Библиотека предоставляет возможность реализовывать автоматически управляемые запросы и мутации декларативно.

Процесс установки описан в документации https://tanstack.com/query/v4/docs/installation на официальном сайте библиотеки.

Для установки пакета через npm менеджер в терминале из папки проекта я использовал команду

npm i @tanstack/react-query

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

@tanstack/react-query: 4.3.4

axios: 0.27.2

4. Настройка react-query клиента на уровне приложения

После добавления react-query в зависимости, возможно его импортирование и дальнейшее использование.  Все компоненты, которые будут использовать импортируемые из данной библиотеки методы должны находиться в структуре компонентов внутри импортируемого из библиотеки компонента QueryClientProvider. Принимающий обязательный  пропс queryClient, который содержит созданный экземпляр QueryClient из этой же библиотеки со всеми необходимыми параметрами.

Так как нам нужно обеспечить клиент данной библиотеки на уровень всего приложения, то в рамках данного подхода, предлагается встроить его на уровень компонента App.js, отредактировав его содержимое.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
 return  <QueryClientProvider client={queryClient}>
         <div />
     </QueryClientProvider>
};
export default App;

Таким образом, в рамках данного подхода разработчик сможет использовать возможности queryClient во всех компонентах внутри приложения, вне зависимости от их количества и нахождения в структуре приложения, кэшироваться и хранить данные. Все запросы будут иметь свой ключ и если запрос уже был сделан и время кэширования данных не было закончено, то данные будут браться из памяти, не придется делать дополнительные запросы. В таком случае пользователь будет быстрее получать данные и экономить трафик.

Эта библиотека открывает много возможностей для менеджмента запросов, все ее возможности описаны в официальной документации.

5. Определение структуры приложения и создание компонентов

Требуется определить удобную для разработки структуру приложения, которую будет возможно масштабировать и поддерживать без особых сложностей.

Предлагаю основное содержимое оставить так же в папке src, внутри проекта, но выделить отдельную папку для хранения компонентов, назвать её components, в которой на каждый новый компонент будет создана отдельная папка с наименованием, отображающим суть компонента, которая будет содержать сам компонент и все сопутствующие ему вспомогательные файлы, которые использует данный компонент. Это позволит при дальнейшем сопровождении приложения быстро сориентироваться в структуре и быстро определить в какой файл требуется внести правки и где он находится в структуре приложения.

В рамках данной статьи я показываю пример реализации приложения, отображающего координаты Международной космической станции используя открытое API http://open-notify.org/Open-Notify-API/ISS-Location-Now/, но возможно использовать любое другое, в том числе разработанное вами, так как в рамках статьи стоит цель расскрыть удобный и быстрый способ организации  именно front-end  часть веб приложения на базе React.js, с удобным взаимодействием с сервером.

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

Наиболее подходящие название для компонента, как мне кажется, является coordinates. Я создам папку с соответствующим названием внутри папки components, где создать файл index.jsx, как основной. При необходимости создания других компонентов данную процедуру просто потребуется повторить.

При этом для удобства функцию, выполняющую запрос я вынесу в отдельный файл в папке компонента и назову его requests.js, в котором напишу и экспортирую функцию для хука useQuery, импортируемого из библиотеки react-query.

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

import axios from 'axios';
export function getData() {
   return axios.get('http://api.open-notify.org/iss-now.json');
};

Как видно из кода файлов, использование данной библиотеки делает возможность HTTP запросов крайне простым. Для использования других методов, кроме GET, из протокола HTTP можно ознакомиться в документации библиотеки.

6. Разработка главного компонента приложения

Вернемся обратно к главному файлу компонента. В рамках текущего компонента предлагаю выполнить запрос к интерфейсу открытого API, преобразовать данные в удобный для нас вид и выводить актуальные данные с возможностью пользователя принудительно обновлять данные.

В данном компоненте я импортирую все необходимые зависимости, а это: хук useMemo из библиотеки React, для преобразования данных и их мемоизации до момента обновления актуальных данных, useQuery из @tanstack/react-query и созданную ранее функцию getData.

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

Запрос выполнится автоматически.

В результате выполнения хука useQuery возвращается объект с различными значениями, такими как: статус запроса, данные и т.д Весь список возвращаемых элементов описан в документации. Через механизм деструктуризации в JavaScript получаем только те элементы возвращаемого объекта, которые будем использовать, а именно: data и refetch. В data хранится ответ с данными, а refetch – это функция, которая позволяет принудительно сделать повторный запрос и обновить данные.

После этого, в удобном для меня варианте преобразую получаемые данные и мемоизирую их через хук useMemo, импортируемый из библиотеки React.

Далее в разметке jsx, которую возвращает сам React компонент, я прописываю эти данные и добавляю кнопку, по нажатию на которую выполняется refetch.

Получившееся содержимое файла можно увидеть ниже

import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { getData } from './requests.js';
function Coordinates() {
   const { data, refetch } = useQuery(['data'], getData);
   const { latitude, longitude } = useMemo(() => (data?.data?.iss_position || {}), [data]);
   return (
       <div>
           <button onClick={refetch}>refetch</button>
           <div>latitude: {latitude}</div>
           <div>longitude: {longitude}</div>
       </div>
   )
};
export default Coordinates;

Как видно из примера, при использовании данного подхода для решения достаточно сложной задачи предлагается простое решение. Не требуется писать код, чтобы вручную запускать запросы или обновлять содержимое в интерфейсе. Все это достигнуто благодаря совместному использованию библиотек из данной статьи, а также удобной структуре самого приложения.

Далее я экспортирую данный компонент в файл App.js и помещаю его внутрь QueryClientProvider. После чего компонент можно увидеть в приложении, что показано на рисунке 2. На нем можно увидеть кнопку, на которую пользователь может нажать и обновить данные, а также координаты МКС.

Рис. 2

Финальный код файла App.js, с добавленным в него компонентом Coordinates:

import {
   QueryClient,
   QueryClientProvider,
} from '@tanstack/react-query';
import Coordinates from './components/coordinates';

const queryClient = new QueryClient();
function App() {
 return (
     <QueryClientProvider client={queryClient}>
         <Coordinates />
     </QueryClientProvider>
 );
};
export default App;

На данном этапе можно считать приложение завершенным, так как был показан подход к разработке работающей версии приложения, взаимодействия различных библиотек для построения быстро разрабатываемых front-end приложений с возможностью легкого масштабирования и поддержки.

Текущее приложение возможно расширять и дополнять любым количеством компонентов и запросов.

7. Заключение

На примере данного приложения продемонстрирован метод быстрой разработки front-end части веб приложения на базе библиотеки построения интерфейсов React, а также организация подхода взаимодействия с внешними сервисами с удобным менеджментом запросов.

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

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

Рецензент – Чернецкий И. И.

Текст статьи
  1. Документация по библиотеке для разработки интерфейсов React.js – https://ru.reactjs.org/docs/getting-started.html
  2. Документация по библиотеке react-query – https://tanstack.com/query/v4/docs/overview
  3. Документация по библиотеке Axios – https://axios-http.com/docs/intro
  4. Деструктурирующее присваивание – https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
  5. Мартин Р. Чистая архитектура. Искусство разработки программного обеспечения. Автор:
  6. Адам С. Разработка на JavaScript. Построение кроссплатформенных приложений с помощью GraphQL, React, React Native и Electron.
Список литературы