Представляем Tide - нашу библиотеку управления состоянием React

Мы рады сообщить, что наша библиотека управления состоянием для приложений React, Tide, теперь имеет открытый исходный код и доступна на NPM и GitHub.

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

Мы сделали свой первый шаг в мир React еще в 2014 году, когда использовали его для восстановления и улучшения процесса оформления заказа на Tictail. С тех пор мы были его большими поклонниками и использовали его почти во всех наших проектах - от создания нашей торговой площадки, которая отображает как на сервере, так и на клиенте, до постепенной замены старого стека Backbone, который запускает панель инструментов для всех владельцы наших брендов.

Распространенной проблемой, с которой мы столкнулись на ранних этапах разработки наших приложений React, когда они начали расти, было нахождение хорошей структуры кода, которая не находится (и не должна) находиться в наших представлениях. Facebook представил свое собственное решение этой проблемы с архитектурой приложения, которую они назвали Flux.

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

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

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

Итак, мы начали думать на Flux и написали несколько наших собственных утилит, чтобы уменьшить количество шаблонов и упростить работу.

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

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

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

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

Что действительно важно для Tide, так это то, как вы подключаете свои компоненты к состоянию приложения. Все состояние приложения хранится в одном глобальном неизменяемом объекте состояния. Действиям разрешено читать и обновлять его, но представлениям предоставляется доступ только для чтения. Это обеспечивает тот же однонаправленный поток данных, который описан в Flux. Но вместо создания одного или нескольких представлений контроллера, которые заботятся о передаче состояния приложения по дереву, Tide позволяет каждому компоненту определять, что им нужно, из состояния.

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

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

Вот объект состояния приложения:

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

Чтобы объяснить преимущества сохранения этого значения в нашем глобальном состоянии, давайте взглянем на урезанную версию действий в нашем приложении todo:

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

В приведенном выше фрагменте вы увидите, что действие addTodo не содержит параметров - оно может получить все, что ему нужно, из глобального состояния. Удобство этого становится очевидным, когда мы представляем более сложный случай с формой учетной записи пользователя с различными полями ввода для имени, электронной почты, страны, изображения профиля и т. Д. После того, как мы нажмем кнопку сохранения, нам не нужно пытаться собрать все вместе в представлениях перед вызовом действия сохранения, поскольку уровень действия уже имеет доступ ко всем необходимым данным.

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

Последний фрагмент кода, объединяющий этот пример, - это наш компонент поля ввода:

Внизу кода компонента вы увидите, что мы используем функцию wrap, предоставленную в Tide. Это связывает нас с глобальным состоянием, которое в данном случае связывает свойство value с ключом todoInputText в состоянии . Мы также получаем доступ ко всем нашим действиям внутри пространства имен tide в наших свойствах. В остальном это довольно просто - мы вызываем наше действие setTodoInputText при изменении ввода, которое обновляет глобальное состояние, что, в свою очередь, запускает повторную визуализацию компонента. Когда пользователь нажимает клавишу ввода, мы просто вызываем действие addTodo, которое позаботится обо всем остальном.

В приведенном выше примере показано, как состояние, действия и компоненты работают вместе в приложении Tide. Не стесняйтесь ознакомиться с полным примером приложения в нашем репозитории Tide на GitHub.

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

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

То, как React позволяет нам забыть о том, что мы имеем дело с DOM (которая по своей сути является глобальным изменяемым объектом), позволяет нам отделиться от традиционного внешнего образа мышления и перейти к мышлению с точки зрения общей разработки программного обеспечения. Это сделало сообщество более зрелым, чем когда-либо прежде, и мы все рады быть его частью.

Во всяком случае, хватит бессмысленности. Пожалуйста, зайдите в наш репозиторий Tide GitHub и поделитесь с нами своим мнением. Мы рады слышать о любых ошибках, с которыми вы сталкиваетесь, или о функциях, которые вы хотели бы увидеть. Также приветствуются любые предложения в виде запросов на включение!