Прошло много времени с тех пор, как мы начали использовать Typescript в нашем проекте (слава богу!), И фактически он предлагает собственный способ разработки приложений. Дело в том, что ES6 и Typescript предлагают сделать ваш код более объектно-ориентированным. Некоторые люди предпочитают более функциональное программирование на JS, но я чувствую, что мы можем добиться большего на стыке обоих миров - объектно-ориентированного программирования и функционального программирования. В этой статье я хочу поделиться своим способом описания хранилищ redux в моих приложениях ReactJS.

Redux - прекрасная полезная библиотека и, кстати, очень простая, поэтому мы постоянно используем ее с React. Если вы мало знаете о Redux t̶h̶e̶n̶ ̶c̶h̶e̶c̶k̶ ̶t̶h̶e̶ ̶c̶a̶l̶e̶n̶d̶a̶r̶, ̶ ̶i̶t̶’̶s̶ ̶2̶0̶1̶9̶ ̶a̶l̶r̶e̶a̶d̶y̶, я бы порекомендовал понять, как он работает, и я бы попытался понять, как он работает. Для Angular у нас есть нечто похожее под названием NgRx.

Главное, что остается в Redux, - это иметь новый объект состояния каждый раз, когда данные обновляются, поэтому я в конечном итоге вижу такие строки:

Кому-нибудь это нравится? Нет. Мне не нравятся все эти переключатели, даже если Typescript закроет вам спину и попросит реализовать код для каждого типа действия. Но. Самым ужасным здесь для меня является то, как мы манипулируем государством. Мы получаем новое состояние разными способами. Это может быть lodash-ish state = _.clone(currentState) или более стандартное использование оператора распространения, например state = { …currentState }. Или, если у вас более сложная структура с другим редуктором в состоянии, вам нужно клонировать его вручную, поэтому в сложном случае вам понадобится функция клонирования.

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

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

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

Что мы здесь видим? Мы полностью описали наше состояние (спасибо, Typescript!), А также теперь у нас есть статический метод clone(), связанный с определенным классом состояния. Итак, мы на 100% знаем, как его клонировать, и знаем, что произойдет, если мы захотим его клонировать.

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

Я только что определил здесь, что каждый из моих редукторов должен иметь обработчик для инициализации первого состояния, а также должен иметь список действий и обработчик, который этот редуктор должен обрабатывать. Объект changeState делает именно это: он получает действие, ищет подходящий обработчик, если он существует, а затем применяет преобразование. Просто так.

Кстати, я добавил функцию callAction, чтобы запускать действия откуда угодно!

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

Мы определили обработчики по идентификатору действия и попросили создать начальное состояние. Легкий! Теперь мы можем клонировать текущее состояние и преобразовывать его, как захотим. Мы также можем вообще забыть об идентификаторах действий, поскольку у нас есть функции, которые будут вызывать для нас правильные действия!

И в качестве последнего шага нам нужно объединить наш редуктор с другими и настроить наше приложение.

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

Чтобы завершить то, чего мы достигли:

  • Мы используем Typescript для описания нашего состояния Redux, поэтому мы полностью понимаем, что он содержит, и не можем допустить ошибки из-за неправильного хранения;
  • Мы используем классы для сбора связанных вещей, таких как методы клонирования для определенного состояния;
  • Мы используем наследование для извлечения общей логики из наших редукторов, чтобы мы могли повторно использовать ее и позволить конкретному редуктору делать свои собственные вещи;
  • У нас есть четкое понимание того, что происходит, каждый обработчик теперь представляет выделенную функцию, нам не нужно раскрывать внутренние идентификаторы действий где-то в компонентах React;

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

Какой подход вы больше всего предпочитаете при использовании Redux в своих проектах? Обсудим в комментариях!

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