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

Спланируйте форму своего состояния

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

  • Как он будет хранить несколько ресурсов из API (пользователи, учетные записи, элементы и т. д.)?
  • Как он будет обрабатывать состояния загрузки (показывая счетчики загрузки при извлечении/обновлении данных)?
  • Как он будет обрабатывать отображение и очистку уведомлений об успешном выполнении и ошибках пользовательского интерфейса?
  • Это кажется последовательным и предсказуемым? Может ли другой член команды легко работать с ним?
  • Легко ли получить доступ к данным внутри него? Вкладывает ли он свойства без необходимости?
  • Это сериализуемо? Можно ли его легко хранить в локальном хранилище или в базе данных?
  • Есть ли какие-либо свойства, которые вы могли бы извлечь из URL-адреса, а не из состояния?
  • Нет ли здесь дублирующихся данных? Если да, то действительно ли это нужно?

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

Избегайте вложенных объектов состояния

Некоторые приложения Redux имеют глубоко вложенные структуры состояний, то есть фигуры, которые выглядят так:

{ foo: { bar: { baz: { qux: ... } } } }

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

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

Сохранение необработанных данных в состоянии

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

В приложениях Redux на самом деле есть два типа данных. Во-первых, это необработанные данные, данные, необходимые вашему приложению для запуска. Пользовательские данные, полученные из API, являются примером необработанных данных — без них у вашего приложения не будет информации, необходимой для запуска. Второй — производные данные или данные, созданные из других существующих данных. Использование свойств firstName и lastName для отображения имени пользователя как Jane Doe является примером производных данных.

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

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

Предпочитайте состояние Redux, а не состояние React

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

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

Стандартизировать полезные данные действий

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

Обеспечить возможность компоновки создателей действий

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

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

Контейнеры и презентационные компоненты

Самая полезная концепция, с которой я столкнулся для создания стабильных и легко поддерживаемых приложений Redux, — это парадигма контейнер и презентационный компонент, описанная Дэном Абрамовым в официальной документации Redux. Я не буду углубляться в это здесь, так как документы уже отлично объясняют концепцию с помощью отличных примеров. Но понимание этой парадигмы может быть одной из самых полезных вещей, которые вы можете узнать в стране Redux. Без него очень сложно поддерживать и дорабатывать приложение даже средней сложности. Изучите это хорошо.

Использовать промежуточные контейнеры

Хотя парадигма контейнер/презентационный компонент работает, не всегда ясно, когда следует вводить контейнеры. Я видел (и писал) приложения с одним контейнером верхнего уровня, который извлекает весь мир, а затем передает все дочерним элементам своего компонента и их дочерним элементам. Это приводит к тому, что реквизит «проходит через» несколько компонентов еще до того, как они будут использованы. По мере роста вашего приложения это становится раздражающей проблемой, так как даже простые изменения, такие как переименование свойств, требуют изменения многих других несвязанных компонентов. Определенно код пахнет, что что-то не так.

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

Нет правил

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

Первоначально опубликовано на сайте joeellis.la 20 июня 2017 г.