Управление состоянием: Jotai вместо Redux

Самая утомительное при реализации Redux  —  это написание того объёма шаблонного кода, который нужен для обработки потока данных между компонентами и хранилищем Redux. Сам Redux был создан по примеру появившейся в 2014 году архитектуры потока данных Flux.

С момента выхода React произошёл переход от компонентов класса к функциональным компонентам с хуками. Несмотря на все преимущества Redux и Flux (это замечательные инструменты), возможно, стоит задействовать новый шаблон, который в полной мере раскрывает возможности React.

Jotai  —  это библиотека управления состоянием для React, которая избавит вас от мучений с шаблонным кодом благодаря своему минималистичному API. Причём с Jotai никаких экшенов, редьюсеров или диспетчеров создавать не придётся!

Специфика работы Jotai очень схожа с React Context и Recoil. Быстро разобраться с ней не составит большого труда, даже если вам не знакомы эти названия, потому что эта библиотека очень проста.

Как работает Jotai

Для начала работы с Jotai первым делом нужно установить эту библиотеку:

npm install jotai
# или
yarn add jotai

Jotai заменяет значения локального состояния в компонентах React на объекты atom, которые содержат часть состояния. Затем этот объект atom будет передан в хук useAtom(), чтобы компоненты могли использовать и обновлять значение, которое в тот момент будет в нём храниться. Объект atom создаётся с помощью вызова функции atom:

import { atom } from 'jotai'

const countAtom = atom(0)
const countryAtom = atom('Japan')

Теперь эти atom (0 и Japan) хранятся внутри Jotai в компоненте Provider, который используется для обёртывания компонентов React:

import { Provider } from 'jotai'

const Root = () => (
  <Provider>
    <App />
  </Provider>
)

В этом примере все компоненты, обёрнутые в Provider, теперь имеют доступ к объектам atom в Jotai и обновляют их.

Доступ к atom получается с помощью хука useAtom, возвращающего значение atom и функцию обновления. Очень похоже на хук useState():

import { useAtom } from 'jotai'

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  return (
    <>
      <h1>{count} </h1>
      <button onClick={() => setCount((value) => value + 1)}>one up</button>
    </>
  );

Вот и всё! atom вызывается из любых других компонентов, лишь бы они находились внутри Provider в дереве DOM.

По сравнению со структурой приложения React-Redux, React-Jotai намного проще. Вот как выглядит типичная структура React-Redux:

React-Redux вкратце

А вот простая схема, иллюстрирующая работу Jotai:

React-Jotai вкратце

Для просмотра шаблона Jotai доступна интерактивная демо-версия. Рекомендуется выполнить объявление atom в отдельном файле: так эти atom смогут импортироваться в любые компоненты consumer.

Рефакторинг приложения на Redux для использования Jotai

Трудность выполнения рефакторинга приложения на Redux для использования Jotai зависит от сложности самого приложения. За образец возьмите эти идентичные примеры:

Выполняя рефакторинг, первым делом обычно меняют Provider в Redux на Provider в Jotai. С этого:

import { createStore } from "redux";
import { Provider } from "react-redux";
import App from "./App";

const store = createStore(reducer);

const Main = () => (
 <Provider store={store}>
    <App />
  </Provider>
)

На этот:

import { Provider } from "jotai";
import App from "./App";

const Main = () => (
  <Provider>
    <App />
  </Provider>
)

Больше нет необходимости передавать store в Provider, но для замены механизма store нужно создать atoms. Если у вас в редьюсере применяется defaultState, используйте его для создания atoms:

const defaultState = {
  isPrimary: true,
};

const reducer = (state = defaultState, action) => {
  // остальная часть кода опущена...
}

Этот редьюсер можно заменить на простую функцию atom:

import { atom } from "jotai";

export const isPrimaryAtom = atom(true)

Разобравшись с той частью, что относится к Provider и Store, для завершения рефакторинга нам остаётся только заменить Dispatcher и Action. Общий шаблон Dispatcher должен выглядеть так:

import { useSelector, useDispatch } from "react-redux";

function App() {
  const isPrimary = useSelector((state) => state.isPrimary);
  const dispatch = useDispatch();

const title = isPrimary ? "Primary" : "Secondary";

return (
  <StyledButton
   isPrimary={isPrimary}
   onClick={() => {
    dispatch({ type: "TOOGLE_BUTTON", payload: !isPrimary });
   }}
  >

Нужно заменить импорт на useAtom из Jotai и для замены Dispatcher использовать возвращённое состояние из функции обновления:

import { useAtom } from "jotai";
import { isPrimaryAtom } from "./Atoms";

function App() {
  const [isPrimary, setIsPrimary] = useAtom(isPrimaryAtom);

const title = isPrimary ? "Primary" : "Secondary";

return (
  <StyledButton
   isPrimary={isPrimary}
   onClick={() => {
    setIsPrimary(!isPrimary);
   }}

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

Заключение

Jotai и Recoil прекрасно демонстрируют то, как хуки повлияли на разработку приложений на React. Несмотря на муторный шаблонный код, Redux продолжает быть самой известной библиотекой управления состоянием, потому что её преимущества весомее затрат. Но с Jotai эти преимущества теперь получают без затрат.

Тем не менее это не означает, что Redux больше не нужна. Многие годы её безуспешно пытались заменить другие инструменты управления состоянием. В случае если приложению требуется ручной контроль над тем, когда, почему и как изменяется состояние, Redux будет незаменима. С другой стороны, если вам нужен простой инструмент управления состоянием для использования этого состояния глобально, то Jotai может быть отличной альтернативой.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Nathan Sebhastian: Redux-Free State Management with Jotai