null

Хранение и обработка состояния React-приложения в виде query-параметров. Часть 1.

Введение

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

Однако, использование useState имеет недостатки. Одним из них является тот факт, что хранимое в компонентах состояние не сохраняется с обновлением страницы, что может ухудшить UX веб-приложения. 

Проблема

Представьте, например, что вам потребовался новый компьютер (потому-что ваш старый комп начал безбожно тупить), и вы зашли на сайт с каталогом техники для поиска подходящей модели. У вас, как у уверенного ПК-пользователя имеется целый ряд критериев к новому девайсу. И вот вы указываете в фильтрах объем оперативной памяти, частоту процессора, модель видеокарты, цену, и много-много чего ещё…

И вот вы, потратив своё драгоценное время на подборку параметров, которые подходят именно вам, наконец определились с критериями и наконец смотрите подходящие варианты. Но вдруг вы случайно обновили страничку и… Все ваши фильтры сбросились. Согласитесь, ситуация мягко говоря неприятная, и ласковых слов разработчикам за такое поведение сайта точно не скажешь. 

Query-параметры

Как можно предотвратить подобный сброс состояния приложения? Одним из способов решения проблемы может стать использование URL-строки браузера, а точнее её query-параметров:

https://website.ru/catalog?cpu_cores=6

Здесь мы храним единственный фрагмент состояния поиска - нужное количество ядер процессора – gpu_cores. Одного этого параметра для тщательного подбора может не хватить, поэтому можно добавить дополнительные параметры:

https://website.ru/catalog?cpu_cores=6&ram_size=32&gpu_model=rtx_3090

Данный пример уже выглядит более интересно, поскольку содержит в себе сразу 3 параметра – cpu_cores, ram_size и gpu_model.

Дополнительные возможности

Ещё одно преимущество, которое дает хранение состояния в URL – возможность сохранения состояния. На нашем примере с сайтом по подбору техники это даст возможность, например, сохранения в закладках браузера страницы с подготовленными фильтрами, что позволит вернуться пользователю на страницу с подготовленными фильтрами! 

Хранение состояния приложения в URL также позволит пользователям веб-приложения делиться друг с другом страницами с подготовленным состоянием – если, например, вы подобрали значения фильтров на странице и решили поделиться страницей с другом, то он при открытии сайта сможет увидеть контент на странице, подобранный по вашим параметрам фильтрации.

Библиотека use-query-params

Подготовка

Для работы библиотеки use-query-params в первую очередь её необходимо загрузить, сделать это можно с помощью команд:

npm i use-query-params

или

yarn add use-query-params

или

bun i use-query-params

Использование данной библиотеки предполагает, что вы используете React Router в качестве решения для маршрутизации react-приложения. 

Для работы библиотеки необходимо обернуть приложение в компонент QueryParamProvider, предоставив адаптер для соответствующего router'а. Ниже представлен пример данной настройки с официальной страницы библиотеки:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryParamProvider } from 'use-query-params';
import { ReactRouter6Adapter } from 'use-query-params/adapters/react-router-6';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <BrowserRouter>
    <QueryParamProvider adapter={ReactRouter6Adapter}>
      <Routes>
        <Route path="/" element={<App />}>
      </Routes>
    </QueryParamProvider>
  </BrowserRouter>,
  document.getElementById('root')
);

Обратите внимание, что QueryParamProvider находится внутри BrowserRouter, а не наоборот – такое расположение важно для корректной работы библиотеки.

useQueryParam

В рамках использования URL-параметров для хранения состояния можно воспользоваться библиотекой use-query-params. Данная библиотека предоставляет хук useQueryParam, работающий схожим образом с хуком useState:

import { FC } from 'react';
import { useQueryParam, NumberParam, StringParam } from 'use-query-params';

const QueryParamExample: FC = () => {
	const [cpuCores, setCpuCores] = useQueryParam('cpu_cores', NumberParam);
	const [gpuModel, setGpuModel] = useQueryParam('gpu_model', StringParam);

	return (
		...
	);
}

В данном примере мы используем хук useQueryParam для обработки двух параметров – cpuCores и gpuCores, в качестве аргументов, передаваемых хуку, указывается название переменной в URL и тип параметра. 

В качестве возвращаемого значения хук отдает значение состояния и функцию её изменения. При этом тип значения, возвращаемого хуком, зависит от указанного типа параметра – cpuCores будет иметь тип number | null | undefined, а gpuModel будет иметь тип string | null | undefined.

Библиотека предоставляет список поддерживаемых типов параметров, среди которых числовые и строковые примитивы, булевые значения, объекты Date, массивы, JSON-объекты и т.д. – с полным списком можно ознакомиться на странице документации.

При вызове функции изменения состояния, возвращаемой хуком useQueryParam, значение соответствующего параметра будет изменяться не только в рамках состояния приложения, но и в рамках значения указанного URL-параметра.

Например, вызов setCpuCores(8) приведет к изменению URL-адреса:

https://website.ru/catalog?cpu_cores=6

После этого,  при вызове setGpuModel('radeon_r6') получим:

https://website.ru/catalog?cpu_cores=6&gpu_model=radeon_r6

При обновлении страницы с указанными URL-параметрами значениям параметров cpuCores и gpuModel будут автоматически присвоены значения из URL.

useQueryParams

В примере работы с хуком useQueryParam мы использовали его для отслеживания состояния двух параметров, относящихся к фильтрации компьютеров. Однако нет никаких сомнений, что для подборки ПК может потребоваться большее количество различных параметров. Что же, в таком случае писать для каждого из них свой useQueryParam? Не было бы разумнее объединить схожие по назначению параметры в один объект и использовать его? Такую задачу позволяет решить хук useQueryParams.

import { FC } from 'react';
import { useQueryParam, NumberParam, StringParam } from 'use-query-params';

const QueryParamsExample: FC = () => {
	const [cpuCores, setCpuCores] = useQueryParam('cpu_cores', NumberParam);
	const [gpuModel, setGpuModel] = useQueryParam('gpu_model', StringParam);
	const [filters, setFilters] = useQueryParams({
		cpu_cores: NumberParam,
		gpu_model: StringParam,
		ram_size: NumberParam,
	});
	const { cpu_cores: cpuCores, gpu_model: gpuModel, ram_size: ramSize } = filters;
	return (
		...
	);
}

В данном примере возвращаемый объект filters будет содержать в качестве атрибутов параметры cpu_cores, gpu_model и ram_size. Деструктуризировав filters, можно получить значения фильтров.

В данном примере при вызове setFilters({cpu_cores: 12, gpu_model: 'rtx4080', ram_size: 32}) произойдет изменение значения этих параметров в URL-адресе:

https://website.ru/catalog?cpu_cores=12&gpu_model=rtx4080&ram_size=32

При вызове, например,  setFilters({ram_size: 64}), по-умолчанию произойдет изменение только параметра ram_size, значения остальных параметров останутся прежними:

https://website.ru/catalog?cpu_cores=12&gpu_model=rtx4080&ram_size=64

Заключение

В данной статье были кратко рассмотрены базовые возможности библиотеки use-query-params для решения задачи синхронизации состояния React-приложения с query-параметрами URL-адреса. В действительности, эта библиотека умеет больше, чем было показано здесь, поэтому, если вас заинтересовала возможность данного инструмента, welcome

Однако, при использовании данной библиотеки имеется ньюанс – как вы могли заметить, use-query-params работает только при использовании React Router. А что, если, по каким-либо причинам, на вашем проекте не используется этот инструмент маршрутизации? В таком случае предлагаю вам реализовать собственный хук обработки query-параметров, о чем пойдет речь в следующей части.

 

Previous Next

Коротко о себе:

 


Frontend-разработчик в компании Tune-it.

Nothing has been found. n is 0