В своей работе фронтенд-разработчиком я видел много Redux-кода, и, честно говоря, иногда меня от него тошнит. Почему? Проблема в том, как мы используем Redux. Разберемся, что с ним не так.

ВНИМАНИЕ: эта статья не о том, как избавиться от Redux. Это всего лишь мои мысли и то, как мы должны использовать этот инструмент.

Давайте выясним, что нам должна дать хорошая библиотека менеджера состояний?

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

Можем ли мы использовать только встроенные решения для управления состоянием приложения?

React Context API и Redux — это не одно и то же, да, мы можем использовать оба для управления глобальным состоянием, но это зависит от вашего приложения. Я видел проекты, когда люди начали реализовывать свой собственный Redux на основе React Context.

Например, мы знаем, что когда данные в React Context изменяются, это вызывает повторный рендеринг для всех компонентов, использующих этот контекст, поэтому нам нужно реализовать собственный хук useSelector и другие функции, которые уже может предоставить нам Redux.

Ознакомьтесь с разделами Истоки Redux: история создания популярной библиотеки и Почему React Context не является инструментом «управления состоянием (и почему он не заменяет Redux)».

Поэтому, если нам недостаточно React Context, мы придерживаемся Redux или любой другой библиотеки управления состоянием, потому что они обеспечивают нам оптимизацию. Но подождите, у нас с этим проблема. Разберемся, почему?

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

Давайте посмотрим поток Redux.

Redux дает нам один магазин. Это инструмент для хранения и изменения состояния. Состояние — это данные в нашем приложении и это состояние мы можем изменить только набором операций (действий).И все это работает синхронно.

Синхронности нам недостаточно, наши приложения должны работать с данными с сервера.

А для реализации асинхронных действий нам нужно использовать ПО промежуточного слоя, такое как Redux Saga, Redux Thunk или что-то в этом роде. И тут начинаются проблемы!

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

Состояние клиента и состояние сервера

Состояние клиента (UI) это любая информация, относящаяся к сеансу вашего веб-браузера, например, пользователь может изменить тему (светлая или темная).

Состояние сервера — это данные, которые хранятся на сервере, но должны отображаться на клиенте, например сообщения в блогах или информация о пользователях, которая хранится в базе данных.

«Кэш сервера — это не то же самое, что состояние пользовательского интерфейса, и с ним следует обращаться по-другому».

Вот отличная статья об этом Кента С. Доддса Моя ошибка управления состоянием

Также ознакомьтесь с разделом Управление состоянием приложения React.

Почему мы не должны использовать Redux для состояния сервера (кеша)?

Как я упоминал ранее, Redux прост. Поэтому мы также добавляем такие инструменты, как Redux Saga, для работы с асинхронностью, когда мы хотим получить данные с сервера и отобразить их.

Да, это дает нам возможность загрузить данные с сервера, но нам также нужно работать и управлять этими данными. Redux и async middleware не дают нам никаких инструментов для этого.

И для этого нам нужно написать много шаблонного кода. Я имею в виду многое.

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

Может быть, с самого начала вы можете сказать, что вам все это не нужно, но это до тех пор, пока ваше приложение не вырастет.

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

Только в одном конкретном компоненте? Хм, в какой-то момент мы можем обнаружить проблему зависимости скрытого компонента. Это означает, что если мы удалим этот компонент или никогда не будем его использовать, наши данные никогда не будут загружены. Но эти данные нужны для второго компонента, который до сих пор используется в нашем приложении.

В корне приложения? Но что, если мы не будем посещать эти страницы, где используются эти данные?

В каждом компоненте? Кажется правильным, потому что все эти компоненты становятся независимыми, и нам не нужно заботиться о том, были ли эти данные загружены раньше или нет, мы загружаем эти данные в компонент. Следующая проблема заключается в том, что если мы уже загрузили эти данные? Как мы можем справиться с этим? Итак, мы проверяем, есть ли у нас данные в состоянии, и если нет, мы их получаем. Много работы, не так ли? И нам нужно реализовать это в каждом приложении.

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

Redux Toolkit может помочь нам сократить шаблонный код. Я очень впечатлен Redux Toolkit и хотел бы использовать его в своих следующих проектах, но иногда этого недостаточно. Чтобы узнать, как использовать Redux Toolkit, ознакомьтесь с Изучите современный Redux с помощью Redux Toolkit! (совместно с Марком Эриксоном) и Modern Redux with Redux Toolkit

React Query, SWR, Apollo Client, RTK Queryвсе эти инструменты приходят нам на помощь. Здесь можно найти сравнение.

Что такое React Query, SWR, Apollo Client, RTK Query?

Это библиотеки для управления состоянием сервера, чтобы ваше приложение React поддерживало кеш данных сервера на клиенте. Таким образом, это управление данными, и ваша задача — указать, когда вы хотите обновить кеш новыми данными с сервера.

Также проверьте: