Появление библиотеки RxJS открыло массу новых возможностей в мире JS. Цель RxJS — достигать многого, используя небольшое количество кода. Прочитав эту статью, вы узнаете, как осуществлять обмен данными между компонентами приложения на React, применяя возможности RxJS.
Совет: используйте Bit для организации React-компонентов и обмена ими. Это позволит вашей команде быстрее разрабатывать свои приложения. Просто попробуйте.
React Components Collection
Redux
Обмен данными между несвязанными React-компонентами — это то, ради чего были созданы библиотеки управления состояниями. Существует множество шаблонов для управления состояниями, но наиболее известны два: Flux и Redux.
Redux популярен из-за своей простоты и использования чистых функций. Ведь благодаря им можно не сомневаться, что применение редьюсеров не приведет к каким-то побочным эффектам.
Работая с Redux, первым делом мы создаем централизованное хранилище данных:
Далее связываем компоненты с этим хранилищем и при желании обновляем или удаляем состояние. Любые изменения, внесенные в хранилище, будут отражены в компонентах, связанных с ним. Таким образом, поток данных распространяется на все компоненты, независимо от степени их вложенности. Компонент, находящийся на энном уровне иерархической структуры, способен передавать данные компоненту самого высокого уровня. Последний, в свою очередь, может передавать данные компоненту 21 уровня.
RxJS
С появлением RxJS использовать библиотеки управления состояниями стало гораздо проще. Многим понравился паттерн «Наблюдатель» (Observer), предоставляемый RxJS.
Мы просто создаем поток
Observable
и даем возможность всем компонентам прослушивать его. Если какой-то компонент добавляется к потоку, прослушивающие (или «подписанные») компоненты реагируют на обновление DOM.Установка
Создаем приложение на React, используя
create-react-app
. Если у вас нет create-react-app
, то сперва установите его глобально:npm i create-react-app -g
Далее генерируем проект в React:
create-react-app react-prj
Переходим в директорию:
cd react-prj
Устанавливаем библиотеку rxjs:
npm i rxjs
У нас должен появиться файл, создающий новый экземпляр
BehaviourSubject
.Почему мы используем BehaviorSubject?
BehaviorSubject
— это один из Subject в библиотеке RxJS. Будучи дочерним компонентом Subject, BehaviorSubject
позволяет множеству наблюдателей прослушивать поток, а также делает массовую рассылку событий этим наблюдателям. BehaviorSubject
сохраняет последнее значение и передает его всем новым подписанным компонентам.Таким образом,
BehaviorSubject
:- Позволяет осуществлять массовую рассылку.
- Хранит последние значения, опубликованные подписчиками, и делает массовую рассылку этих значений.
В папке src находится файл
messageService.js
, экспортирующий подписчику экземпляр BehaviorSubject
и объект messageService
. Объект-подписчик создается в начале файла — так он доступен для любого импортирующего компонента. У объекта messageService
имеется функция отправки, принимающая параметр msg
: в нем содержатся данные, которые нужны для передачи всем прослушивающим компонентам. В теле функции мы вызываем метод emit
. Он осуществляет массовую рассылку данных подписанным компонентам в объекте-подписчике.Предположим, что у нас есть следующие компоненты:
- ConsumerA;
- ConsumerB;
- ProducerA;
- ProducerB.
В иерархической структуре они выглядят так:
Компонент приложения передает сообщение ProducerA и ConsumerB. ProducerA отправляет данные ConsumerA, а сообщение от ConsumerB попадает к ProducerB.
Компоненты ConsumerA и ConsumerB имеют индивидуальный счетчик состояния. В их методе
componentDidMount
они подписаны на один и тот же поток subscriber
. Как только публикуется какое-либо событие, у обоих компонентов обновляется счетчик. У ProducerA и ProducerB есть кнопки
Increment Counter
и Decrement Counter
, которые при нажатии выдают 1
или -1
. Подписанные компоненты ConsumerA и ConsumerB подхватывают событие и запускают свои функции обратного вызова, обновляя значение счетчика состояния и DOM.Посмотрим на иерархическую структуру еще раз:
ProducerB передает данные ConsumerA, хотя они абсолютно не связаны. ProducerA передает данные ConsumerB, не являясь его родительским компонентом. В этом вся суть RxJS: мы просто создали центральный узел потока событий и позволили компонентам прослушивать его. Когда какой-либо компонент генерирует события, прослушивающие компоненты тут же подхватывают их.
Поиграть с приложением можно на stackblitz: https://react-lwzp6e.stackblitz.io
Заключение
Итак, мы увидели, как можно осуществлять обмен данными между React-компонентами, применяя RxJS. Мы использовали
BehaviourSubject
для создания централизованного потока данных, а затем позволили остальным компонентам подписаться на этот поток. Теперь, когда один из компонентов генерирует данные, прочие компоненты также получают их. Уровень компонентов в иерархической структуре неважен.Если у вас есть вопросы относительно этой темы или вы хотите, чтобы я что-то добавил, исправил или удалил, напишите об этом в комментариях, в электронном письме или в личном сообщении.
Спасибо за внимание!
Комментарии (19)
JustDont
24.05.2019 14:05В показанном разрезе неясно, зачем это вообще надо. Если вы всё равно в каждом условно-сложном компоненте руками работаете через реактовый собственный стейт, то нафига вообще тащить RxJS сюда? Если у вас глобальный объект-хранилище, к которому вы руками лезете из каждого места приложения чтоб подписаться или запустить действие — то, опять же, нафига тут весь RxJS? Тут хватит простейшей наколенной подписочной логики — вместо того, чтоб руками написать 1 коллекцию (подписок) и 1 цикл (прохода по коллекции и дерганья каждой подписки) предлагается подключить RxJS целиком? Серьезно?
Я вообще нисколько не против, просто статья ну совершенно не демонстрирует ничего серьезного, что можно б было сделать через RxJS. То, что продемонстрировано — это полный детсад вида «подключим lodash потому, что я не знаю, что такое ...».serf
24.05.2019 15:28-1Если у вас глобальный объект-хранилище, к которому вы руками лезете из каждого места приложения чтоб подписаться или запустить действие — то, опять же, нафига тут весь RxJS?
RxJS нотификейшен будет триггером перересовки. Тут ведь можно использовать множество операторов, например distinctUntilChanged для предотвращения лишних отрисовок (игнорировать холостные для определенного компонента циклы изменения стора).JustDont
24.05.2019 15:59Так вот об этом и надо писать. А не о том, что получили нотификейшен, дернули setState, и такие сидим радуемся о том, какая же у нас хорошая интеграция RxJS с реактом.
serf
24.05.2019 18:14Все с чего-то начинают.
PS я только сейчас заметил что это перевод, это объясняет некотрые вещи, они там на западе любят постить в блоги поверхностную информацию лишь бы показать активность блога.JustDont
24.05.2019 18:17+1Я категорически против того, чтоб в эпоху нынешнего веба с гуглопочтой на 6.7Мб люди «начинали» на этой ниве с подключения немелкой библиотеки примерно низачем.
PS: Дело не в собирательном «западе», а в том, что это сейчас повсеместно. Вон на хабр посмотрите. У нас соотношение статей полезных к статьям из воды или в стиле КО — тоже не слишком-то хорошее.serf
24.05.2019 18:19Добавил выше PS. Не факт что автор использует такой подход, на западе это норма писать поверхностные блог-статьи.
mayorovp
24.05.2019 14:55Я бы не рекомендовал использовать примеры кода из этой статьи как образец (хвала автору, он выложил их картинками чтобы максимально затруднить это).
Замеченные мною ошибки:
в первом файле явно предполагалось, что другие будут подписываться на
subscriber
, а публиковать значения черезmessageService
. Остальные файлы используют толькоsubscriber
, а проmessageService
все забыли;
странное наименование: вот кем надо быть, чтобы назвать источник данных словом
subscriber
?
используемый
BehaviorSubject
предназначен для передачи значений, а не дельт между ними. Обратите внимание на его основное свойство: воспроизведение последнего полученного значения для новых подписчиков. Какой смысл это свойство имеет для дельты? Для передачи подобных сообщений нужно использовать простойSubject
!
а подписку отменять кто за вас будет?
serf
24.05.2019 15:23BehaviorSubject имеет смысл использовать как реактивный центральный стор. То есть это не просто передача данных но и фиксация последнего состояния. Но нужно использовать именно как единый/центральный стор, иначе получится каша.
mayorovp
24.05.2019 15:26Не обязательно единый центральный, там есть варианты. Но да, он именно что фиксирует состояние. А у автора состояние — своё локальное у для каждого подписчика, глобального состояния вообще нет. Потому и BehaviorSubject использовать нельзя.
xState_level80
24.05.2019 20:43Context API, hooks API, redux, mobx… и тут ещё RxJs для управления стейтом. Как начинающий фронтэндщик я уже давно в шоке!
mayorovp
24.05.2019 21:38+1Что значит "и тут ещё"? RxJs древнее реакта...
xState_level80
25.05.2019 16:24mayorovp Имелось в виду для использования в качестве глобального состояния
mayorovp
25.05.2019 17:08В качестве глобального состояния rxjs можно было использовать с самого начала
serf
25.05.2019 11:19-1Дело в том что React создавалась как библиотека продвинутый-шаблонизатор. Но народ начал использовать это изделие как составную часть самособранных-фреймворков. В итоге вылезло множество граблей которых нет во взрослых фреймворках, и в React соответственно начали добавлять множество костылей чем и до сих пор занимаются.
Ni55aN
27.05.2019 11:46В некоторых кейсах такой глобальный сервис не подойдет, почему бы сразу не сделать это через DI? Допустим, пробросить экземпляр в провайдер Context API, причем только на необходимом уровне иерархии. В итоге, приходим к том, что уже давно умеет Angular и Vue
staticlab
Отписываться от сообщений при удалении компонентов не нужно?
psFitz
Нужно