Это снова Алекс, чтобы рассказать о советах и ​​передовых методах, которые мы усвоили в Delve за последние ~ 18 месяцев выпуска кода React и Flux. Хотя React и Flux, естественно, являются отличными ямами успеха, есть некоторые вещи, которые вы можете сделать, чтобы углубить эту яму. Ни один из них не является революционным или даже новым, но иногда полезно записать их в одном месте.

Пирамида React

В Delve мы пытаемся реализовать три типа компонентов React: Контейнеры, Умные и Тупые. Эта дифференциация во многом вдохновлена ​​Дэном Абрамовым. Наши компоненты React организованы в такую ​​иерархию:

Наверху находится React Router, который обычно используется приложением React для управления маршрутами или страницами. В случае Delve мы понимаем, что каждый маршрут соответствует компоненту Контейнер, который является относительно легкой конструкцией. Контейнеры в первую очередь отвечают за то, чтобы на странице были правильные интеллектуальные компоненты, и за потенциальную передачу параметров маршрута, таких как выбранный пользователь, текст запроса и т. Д. Компоненты контейнера визуализируют небольшую реальную DOM; в основном они возвращают один или несколько компонентов Smart в своей функции render (). Примеры контейнеров для Delve включают HomePage, UserPage и SearchPage.

Компоненты Smart названы так, потому что им разрешено взаимодействовать с Stores и Action Creators в нашем шаблоне Flux. Эти компоненты полностью зависят от приложения и очень часто зависят от сценария и / или пользовательского интерфейса. Интеллектуальные компоненты регистрируются в Магазинах, из которых им нужны данные, получают уведомления об обновлениях из Магазинов и преобразуют данные Магазина в реквизиты для визуализируемых ими немых компонентов. Умные компоненты в нашем шаблоне используют setState () и возвращают немые компоненты из функции render () с помощью свойства состояния. Примеры интеллектуальных компонентов: HomeFeed, ModifiedFeed, RelatedPeople и SearchBox.

Тупые компоненты составляют большую часть нашего приложения и могут быть полностью повторно использованы внутри и вне приложения Delve. Эти компоненты ничего не знают о Stores, Actions и Action Creators или что-либо еще об остальной части приложения. Они глупы, потому что знают только об интерфейсе props и типах, которые им передаются. Глупые компоненты - это то место, где мы визуализируем большую часть нашей DOM и определяем CSS для компонента. Мы стараемся думать о Dumb-компонентах как о почти полностью функциональных и не имеющих состояния, хотя есть небольшие исключения, когда немые компоненты должны сохранять состояние вне Store для выполнения своей работы. Тупые компоненты обычно образуют иерархию тупых компонентов, ссылаясь на другие тупые компоненты и используя render (). Применяя правило, согласно которому Dumb-компоненты могут ссылаться только на свойства, мы гарантируем, что большинство наших Dumb-компонентов можно использовать повторно и легко реорганизовать для будущих пользователей. Примерами компонентов Dumb являются FavoritesButton, PersonImage и CardLinkArea.

Приложение Delve на ~ 90% состоит из немых компонентов. Это означает, что наша логика для конкретного приложения содержится в небольшом количестве компонентов / файлов, что делает рефакторинг и повторное использование в приложении чрезвычайно простым и гибким.

У нас также есть фантастические возможности отладки с этой иерархией: довольно легко использовать инструменты React F12 для проверки компонента и определения проблемы. Если реквизиты правильные, то нам следует взглянуть на функцию render () компонента Dumb. Если нет, то мы проходим по иерархии компонентов до интеллектуального компонента и, в конечном итоге, до состояния интеллектуального компонента. Если состояние интеллектуального компонента неправильное, значит, в магазине или в функции setState () интеллектуального компонента что-то не так.

Рекомендации по использованию глупых компонентов

Одна вещь, которую мы быстро поняли после поставки наших первых ~ 50 компонентов React, заключается в том, что вы не можете гарантировать, что все функции render () будут успешно выполняться. Независимо от того, насколько хороши ваши принудительное применение типов или стратегии модульного тестирования, вы столкнетесь с некоторыми исключениями в производственной среде. Когда функция render () генерирует исключение, она всплывает и предотвращает рендеринг потенциально больших разделов вашего приложения. Мы быстро решили, что нам нужно решение, которое позволяет достичь двух целей:

  1. Убедитесь, что ошибка в компоненте React, глубоко в дереве, не приводит к сбою всей страницы.
  2. Зарегистрируйте сбой для анализа ошибок и отладки.

Для решения этих двух задач мы ввели в наш код класс BaseComponent, от которого наследуются все компоненты. Мы применяем шаблон наследования с помощью специального правила tslint. Кажется возможным сделать это и с помощью декоратора, но у нас нет времени исследовать это. Сообщите нам, если вы делаете что-то подобное!

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

Компоненты меньшего размера

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

Есть две причины, по которым мы стараемся избегать этого антипаттерна. Во-первых, SomeComponent станет очень большим компонентом, который будет трудно читать. Вторая причина заключается в том, что мы чрезмерно используем this.props и его интерфейс IGiganticPropList для множества функций, которым не нужен доступ ко всему интерфейсу. Вместе они усложняют повторное использование и рефакторинг этого компонента, чем следовало бы. Благодаря функциональным компонентам React 0.14 это очень легко сделать с самого начала:

Использование более мелких компонентов побуждает нас определять правильные интерфейсы свойств, увеличивает возможность повторного использования и читаемость. Когда мы видим renderXYZ или getXYZ в обзоре кода, это явный сигнал, что мы должны создавать новый компонент React вместо вспомогательной функции.

Рекомендации по интеллектуальным компонентам

Наши интеллектуальные компоненты призваны упростить управление жизненным циклом Store и React. Все компоненты Smart наследуются от базового класса SmartComponent, который обеспечивает эту поддержку. Чтобы определить смарт-компонент в нашем шаблоне, вы указываете, из каких хранилищ вы будете получать данные в конструкторе, а затем переопределяете функцию getState (), которая определяет, как ваш смарт-компонент будет преобразовывать данные из хранилища во внутреннее состояние.

В дополнение к вышесказанному, интеллектуальный компонент, конечно, должен будет определить функцию render (), которая преобразует внутренние элементы состояния в Dumb-компоненты.

Одна проблема, с которой вы можете столкнуться в том, как работают ваши смарт-компоненты, связана с шаблоном вызова ActionCreator.Dispatch (Action) - ›Store.EmitChange () -› SmartComponent.SetState (). Этот шаблон имеет смысл, когда на действие отвечает одно хранилище, но все может быстро усложниться, когда несколько хранилищ ожидают действия, изменяют свое внутреннее состояние и испускают изменение. Что может случиться, так это то, что ваши SmartComponents могут закончить настройку состояния (и рендеринга) несколько раз, поскольку действие обрабатывается последовательно несколькими хранилищами.

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

Отгрузка флюса и обработка склада

Подобно тому, как компоненты React могут иметь ошибки в своих функциях render (), которые вызывают каскадный разрыв из-за необработанных исключений, магазины могут иметь такие же ошибки. Чтобы ограничить влияние этого и позволить нам диагностировать эти ошибки в производственной среде, у нас есть аналогичный базовый класс для наших магазинов, который выполняет логику try / catch / log:

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

В заключение скажу, что React и Flux - это гигантские «ямы успеха», но, как и во всех производственных системах качества, вы должны убедиться, что у вас есть необходимые меры безопасности, чтобы предотвратить ошибки до отправки и диагностировать проблемы после доставки. Сообщите нам, если у вас есть какие-либо советы или хитрости, которые мы упустили!