Как добавить кэширование в ваше React приложение15.07.2024 16:00
Вступление
В современном веб-деве скорость и производительность приложений имеют решающее значение. Пользователи ожидают, что страницы будут загружаться мгновенно, а разработчики стремятся к созданию максимально отзывчивых интерфейсов. Одним из эффективных методов повышения производительности является кэширование данных.
Кэширование позволяет значительно сократить время загрузки и уменьшить нагрузку на сервер, сохраняя часто используемые данные в local storage. В данной статье мы рассмотрим один из способов кэширования в React-приложениях — используя хуки.
Внедрим технологию кэширования в существующее React приложение
Суть приложения: на странице сделаем форму, в которой пользователь впишет id покемона и будут возможны 3 варианта:
Покемон будет обнаружен в кэше и его компонент подгрузится моментально, используя данные из local storage
Покемон не будет найден в хранилище. Тогда мы отправим запрос на сервер и дадим пользователю возможность сохранить данные в кэш
id покемона не будет найден ни в хранилище, ни на сервере — отправим ошибку
Также, данные, которые будут хранится в кэше больше часа (или любого другого времени) будут удалены, чтобы не захламлять хранилище.
Для апи буду использовать https://pokeapi.co/ — моковый апи, который как раз подойдет для наших задач.
Также, воспользуюсь библиотекой reactuse. Это самый большой набор переиспользуемых react хуков. Используя эту библиотеку вы сможете перестать париться о дефолтном веб функционале, ведь эту работу за вас возьмут хуки из пакета. Из reactuse возьмем 3 хука: useLocalStorage, useQuery, useMount.
Ставим библиотеку:
$ npm i @siberiacancode/reactuse --save
# или
$ yarn add @siberiacancode/reactuse
Создадим развертку для формы с input элементом (для ввода id покемона)
Здесь мы используем useState, чтобы хранить значение из поля ввода в состоянии inputText. На onChange обновляем значение этого стэйта до актуального. Также создадим стэйт pokemonId, если в случае с inputText мы просто храним любую информацию, которую пользователь вводит в инпут, то здесь мы уже храним конкректный айди покемона, по которому будем отправлять запрос.
Создаем функцию handleSubmit. Она будет вызываться, когда форма будет отправлена. В ней мы вписываем, что как только форма будет подтверждена — мы запишем в pokemonId значение из нашего поля ввода. Мы используем e.preventDefault () чтобы страница не перезагружалась, как только форма будет отправлена.
Теперь создадним новый компонент Pokemon и в пропсах будем принимать id покемона
Создидим константу, показывающую, через сколько секунд данные из кэша должны удалиться
const CACHE_EXPIRATION_TIME = 3600; // seconds
Используем хук useLocalStorage по ключу «pokemons». И сразу создадим константу cachedPokemon в ней мы будем хранить данные покемона из кэша, по запрошенному айди (если он конечно там имеется)
Мы делаем запрос по айди, который берем в пропсах. Важно: в options хука указываем в keys: айди, чтобы запрос шел заново, как только обновится проп id. Также добавляем enabled: ! cachedPokemon, это можно перевести так: «отправлять запрос только в том случае, если не получилось достать покемона из кэша»
Теперь делаем ряд проверок и отрисовываем ui
if (cachedPokemon)
return (
{cachedPokemon.name}
{cachedPokemon.id}
Loaded from cache
);
Если получилось достать покемона из кэша, то отрисуем его (зеленым текстом допишем, что данные были взяты из хранилища, и подгружаются оптимизировано).
Проверки на ошибку и loading данных:
if (isLoading) return
Fetching Pokemon...
;
if (isError) return
Error: {error?.message}
;
И в случае если не получилось достать покемона и нам пришлось делать запрос на сервер, то отрисуем компонент покемона, добавим кнопку «Save in cache», и красным текстом отметим, что данные были взяты с сервера.
// В expiresAt прописываем текущее время (Date.now ()) + константу CACHE_EXPIRATION_TIME, помноженную на 1000 (чтобы означала секунды)
При нажатии на кнопку создадим отдельную локальную переменную ArrayOfPokemons, равную текущему значению состояния из кэша, добавим в массив запрошенного с сервера покемона (fetchedPokemon) и поместим в сторэдж этот массив, включающий и прошлое состояние из кэша, и нового покемона.
Теперь вернемся в самое начало кода, и перед объявлением useLocalStorage пропишем следующее:
На маунт компонента, мы будем проходиться по массиву покемонов из кэша, и если какой то из покемонов уже expired (Date.now () ≥ expiresAt), то удалим покемона из массива.