Главная » Статьи » Вот более простая альтернатива Redux

Вот более простая альтернатива Redux

От автора: шаблон «Контейнер» — это концепция, представленная в библиотеке Unstated-Next. В шаблоне состояние рассматривается как множество «Контейнеров», которые содержат модульную часть глобального состояния приложения. Чтобы обеспечить это состояние, вы создаете контекст в приложении, затем вы можете получить к нему доступ через хуки.

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

Что такое шаблон контейнера?

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

const {user} = Auth.useContainer();

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

Каждую часть состояния действительно легко представить. Это просто пользовательский хук, подключенный к поставщику контекста. Вот и все. Термин «Контейнеры» на самом деле является просто термином-оболочкой, означающим «Пользовательский хук React + поставщик контекста», поэтому, когда кто-то рекомендует управление состоянием с помощью хуков + useContext, они технически рекомендуют этот шаблон контейнера.

Чтобы использовать контейнеры, вам просто нужно импортировать Context и использовать хук. Вам технически не нужны никакие внешние библиотеки, однако я использую библиотеку Unstated-Next, потому что она дает мне некоторые преимущества, которые делают этот шаблон еще проще.

Что такое Unstated-Next?

Unstated-Next — это крошечная библиотека, которая помогает нам немного легче разбираться в этих глобальных контейнерах. Эта библиотека крошечная (порядка 200 байт), и на это есть веские причины, потому что она в основном не делает ничего сверх того, что уже делает React Context API.

Эта библиотека на 100% необязательна для этого шаблона проектирования. Она просто предоставляет небольшие улучшения API, которые упрощают работу с Context. Некоторые из основных преимуществ включают:

Проверка типа: она дает вам поддержку typescript из коробки. Это была одна из моих проблем с использованием React Context API, поэтому приятно видеть, что unstated-next решает эту проблему.

Обработка ошибок: если вы попытаетесь получить доступ к контейнеру, у которого нет поставщика контекста над ним в дереве React DOM, она выдаст ошибку. Это помогает при отладке.

Легче представлять: размышления о контекстах иногда могут казаться абстрактными, но использовать эту библиотеку с мысленной концепцией «контейнеров» намного проще.

Как выглядит этот шаблон?

Файловая структура

Когда я использую этот шаблон, я помещаю все контейнеры в папку «container» в корне каталога src.

Вот более простая альтернатива Redux

Я добавляю к каждому контейнеру суффикс слова «container» и размещаю весь соответствующий код контейнера в одном файле.

Это уже имеет преимущества по сравнению с чем-то вроде Redux, где единственная задача может быть разделена на 3 или 4 файла для действий, редуктора, хранилища, селекторов и т. д.

Файл-контейнер

Контейнер — это место, где будет размещаться кусочек состояния. Этот файл содержит все необходимое для чтения и записи в эту часть состояния. Вот как может выглядеть файл-контейнер для AuthContainer:

// The reducer. This would be very similar to your reducer in Redux.
// This is optional, you can just use useState instead, but this is
// here to show that if you want to use a reducer and do more
// complicated state transitions you can.
function authReducer(state: AuthState, action: Action) { ...
}
// Custom Hook
function useAuth(initialState: AuthState) { const [state, dispatch] = useReducer(authReducer, initialState);
const loginWithGoogle = () => { dispatch(loggingIn()); doGoogleLogin() .then(user => dispatch(success(user))) .catch(err => dispatch(error(err.message))); }
const loginWithEmailPassword = (email, password) => { dispatch(loggingIn()); doEmailPasswordLogin(email, password) .then(user => dispatch(success(user))) .catch(err => dispatch(error(err.message))); }
const logout = () => dispatch(logout());
return { user: state.data, isAuthenticating: state.loading, error: state.error, loginWithGoogle, loginWithEmailPassword, logout };
}
// Create the Container (this can be a Context too)
// You just pass in the custom hook that you want to build the
// container for.
export const Auth = createContainer(useAuth);

Это действительно чисто, потому что это просто пользовательский хук, а затем немного кода внизу, чтобы сделать его контейнером. Когда вы добавляете этот код внизу, он заставляет этот пользовательский хук иметь одинаковое состояние, даже если он используется в нескольких разных компонентах. Это связано с тем, что контейнеры Unstated-Next просто используют Context API под капотом.

Чтобы это работало, вам сначала нужно добавить в приложение Хранилище, в котором будут храниться все контейнеры. Оно может выглядеть примерно так:

Вот более простая альтернатива Redux

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

Также было бы замечательно, если бы существовал способ сделать все эти нагрузки на одном уровне DOM, чтобы любой контейнер мог получить доступ к любому другому контейнеру, но, к сожалению, я думаю, что это ограничение React.

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

const App: React.FC = () => { return ( <Store> <ReactRouter> <AppRoutes> </ReactRouter> </Store> );
}

И вуаля! Если вы сделали все правильно, теперь у вас должна быть возможность войти в любой из компонентов React и использовать этот хук, как показано ниже:

const LoginPage: React.FC = () => { ... const { formLogin, googleLogin, isAuthenticating, user } = Auth.useContainer(); useEffect(() => { if (user) { history.push('/home'); } }, [user]); ... return ( <div> <button onClick={() => googleLogin()}> Login with Google </button> ... </div> );
}

Если вы все сделали правильно, следование этому шаблону должно подойти для вас! Если вы сделали что-то не так, Unstated-Next может выдать ошибку, в которой говорится, что поставщик контейнера не был создан, но это хорошо, потому что это явное сообщение об ошибке, которое может быть очень сложно отследить, если вы используете базовый контекст React.

Почему бы не использовать Redux?

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

Поэтому я предлагаю этот шаблон как альтернативу.

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

Вот почему существуют React Hooks.

Вы уже можете видеть, как все идет к этой методологии с перемещением других частей состояния из таких вещей, как Redux, в хуки:

Локальное состояние => useState / useReducer

Состояние API => React-Query / useSWR / Apollo

Состояние формы => React Hook Form / Formik

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

Шаблон контейнера реализует эту идею. Он предлагает большую часть функциональности, аналогичной Redux, и разработан с ориентацией на хуки.

Для любого проекта, малого и среднего размера, этот шаблон очень прост. Для более крупного проекта это зависит от варианта использования.

Вот несколько сравнений между шаблоном контейнера и Redux

Шаблон Контейнера имеет следующие преимущества:

Меньше шаблонов, чем в чем-то вроде Redux.

Использует собственный Context API под капотом.

Вы можете изучить API за 10 минут, если знаете useState, useContext и Custom Hooks.

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

Также у него есть следующие минусы:

Нет поддержки промежуточного программного обеспечения.

Нет инструмента, похожего на отладчик Redux chrome.

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

Имея это в виду, надеюсь, теперь вы лучше понимаете, какие существуют альтернативы, если ваш вариант использования не требует чего-то столь громоздкого, как Redux.

Если вы хотите использовать этот шаблон, но не можете полностью отказаться от Redux, другой альтернативой будет использование Redux Toolkit + Redux Ducks Pattern. Подход Redux Ducks хорошо работает, если вы создаете большое приложение, потому что он использует эту методологию, ориентированную на контейнеры, но при этом удерживает вас в экосистеме Redux.

Заключение

Это был шаблон Контейнера. Если вы планируете использовать Redux в приложении, я бы серьезно оценил затраты, связанные с этим, чтобы определить, действительно ли это требуется вашему приложению. Я думаю, что этот шаблон — хорошее место для начала, и поскольку он настолько мал и модулен, вы можете очень легко перенести его в Redux в будущем.

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

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

Автор: Spencer Pauly

Источник: https://dev.to

Редакция: Команда webformyself.