Те из вас, кто использует Redux Toolkit, должны быть знакомы с огромным количеством шаблонного кода, который создается в результате. Наша команда (axmit) пробовала разные обходные пути, немного надеясь, что когда-нибудь мы найдем стандартное решение. Но этого не произошло, и мы решили решить проблему на своей стороне. Мы работали над этим больше года, и окончательный результат описан в этой статье.

История

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

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

  • Каждый разработчик обладает уникальным стилем кода, и достичь «обезличенного кода» сложно;
  • Использование Redux приводит к появлению большого количества шаблонного кода / копипаста и связанных с ним проблем (более 100+ строк кода для CRUD);
  • На разработку даже базовых задач уходит много времени, ведь нужно соблюдать множество правил: создать загрузку, настроить работу с ошибками и т. Д .;
  • Структура различается между разными проектами и даже между разными проектными модулями.

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

Пока руководители проектов изо всех сил пытались найти пару часов в день, чтобы своевременно предоставлять результаты, пара разработчиков воплотила в жизнь простейшую идею. Да, великие идеи всегда кажутся простыми, как только они появляются. Идея заключалась в том, чтобы начать генерировать определенные части кода вместо того, чтобы писать их вручную. Мы не хотели использовать фрагменты, поскольку это привело бы к большому количеству кода (поскольку практически любые изменения в нем привели бы к рефакторингу и многочисленным ошибкам). Вместо этого мы создали функцию, которая будет принимать определенные параметры, и строили на их основе редуктор, действия и сагу. Это была первая версия наших коммуникаций (@ axmit / redux-communications). Название коммуникации появилось несколько естественно, поскольку эта библиотека связывает магазин, саги и компоненты.

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

Покажи мне код

Разговоры дешевы. Покажите мне код , как однажды сказал Линус Торвальдс, и я полностью с ним согласен. Не переписывая документацию и не создавая страниц кода, приведу небольшой пример. Если вам интересно узнать все подробности о библиотеке и примере использования - вы можете найти ссылки в конце этой статьи.

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

  1. Определить транспорт

2. Определите пространство имен

const namespace = ‘task’;

3. Разработайте стратегию общения

const strategy = new CRUDStrategy({ 
   namespace,    
   transport: taskTransport 
});

4. Создавайте общение

const taskCommunication = buildCommunication(strategy);

5. Настройте редукторы и коммуникационные саги.

taskCommunication.reducers 
taskCommunication.sagas

6. Осталось только подключить магазин к компоненту

taskCommunication.injector(MyComponent)

7. Коммуникация готова к использованию!

componentDidMount() {
    const { getTaskCollection } = this.props;
    getTaskCollection(); 
}  
render() {
    const { taskCollection } = this.props;
    return taskCollection.data.map(item => 
        <span>{item.title}</span>
    ) 
}

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

Это все, что вам нужно знать для создания полнофункционального CRUD. Если вам нужно что-то более сложное, связь CRUD может быть расширена или может использоваться BaseCommunication. В худшем случае - старый добрый Redux со всеми функциями все еще остается на крючке. Гибкость по-прежнему сохраняется.

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

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

Почему сейчас?

Я решил поделиться нашим решением после прочтения нескольких статей и документации redux-toolkit. Опробовав этот инструмент, я понял, что общение стало намного проще, и в конечном результате структура магазина стала лучше (хотя и по-прежнему гибкой). После того, как я некоторое время возился с исходным кодом из примера redux-toolkit, я переписал его на сообщения. Я попытался внести минимальные изменения, чтобы вы могли увидеть разницу, хотя все же считаю, что структура исходного кода слишком сложна. Я оставил несколько комментариев, чтобы было легче отслеживать изменения. Обратите внимание на файлы * .communication.ts и фрагменты, которые они заменяют.

То, что количество строк уменьшилось, а код выглядит лучше (субъективный фактор), не так уж и важен, так как у нас довольно тяжелые коммуникации в продакшене. Есть еще одно ключевое отличие. Код декларативный. Мы просто объявляем что хотим получить и что делать с данными; нас совершенно не интересует, как это будет сделано. Подводя итог: redux-toolkit можно использовать для настройки, для всего остального вы можете использовать @ axmit / redux-communications.

Подводить итоги

Преимущества:

  • Код стал стандартизированным во всех проектах и ​​для всех разработчиков. Подобные проблемы имеют аналогичные решения, и обычно они переносятся в отдельные пакеты, которые могут быть повторно использованы в будущем. Количество кода сильно уменьшилось.
  • Младшие разработчики получили четкое представление.
  • Старшим разработчикам не нужно писать тонны стандартного кода, и они работают над улучшением коммуникаций.
  • Отладка и структура магазина стали проще и прозрачнее для всех разработчиков.
  • Переключение между проектами (или модулями проекта) не так болезненно, как раньше.
  • Руководители проектов и клиенты довольны уменьшением количества ошибок. Разработчики тоже довольны - им нужно писать меньше кода.
  • Вы можете переключаться на коммуникации итеративно или даже использовать гибридный подход (коммуникации + срезы).
  • Покупатели получают товары примерно на 20% быстрее.

Конечно, у библиотеки есть и недостатки:

  • Его сложно начать без изучения примеров кода и документации.
  • Настроить структуру магазина невозможно, от этого зависит вся автоматизация. Однако у нас никогда не было проблем из-за этого ограничения.
  • Можно работать только с сагами. Мы всегда ими пользуемся, и Thunk нам не подходил - так что для нас это никогда не было проблемой. Но если саги вам не подходят, пользоваться библиотекой будет невозможно.

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

Полное описание библиотеки можно найти здесь, а протестировать онлайн здесь.

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