Хотя в названии библиотеки React присутствует слово «РЕАКТ», её связь с парадигмой реактивного программирования куда интереснее, чем кажется на первый взгляд. Давайте разберемся, является ли React реактивным в классическом понимании этого слова.
Что такое реактивное программирование?
Для начала, что такое «реактивность»? В широком смысле, это парадигма программирования, ориентированная на автоматическое распространение изменений. Самый простой пример — электронная таблица. Вы меняете значение в одной ячейке, и все ячейки, которые от неё зависят, мгновенно пересчитываются. Им не нужно давать команду «обновись!», они сами реагируют на изменение источника данных.
В программировании эта идея чаще всего реализуется через потоки данных (streams) — асинхронные последовательности событий, которые происходят во времени. На эти потоки можно подписываться и реагировать на них.
Представьте ленту новостей в приложении. Новостной сервер — это Observable (наблюдаемый источник), который испускает события (новые статьи). Вы, как пользователь, — это Observer (наблюдатель), который подписан на этот источник и получает обновления, как только они появляются. Источник «проталкивает» (push
) данные к наблюдателю. Это классическая, push-based модель реактивности, которую популяризировали такие библиотеки, как RxJS.
Ключевая особенность этой модели — полный контроль над потоком. События можно фильтровать (filter
), преобразовывать (map
) и даже группировать по времени (например, оператор bufferTime
в RxJS может собрать все события за 2 секунды и выдать их одной пачкой).
Как работает React?
React построен вокруг простой идеи: ваш UI — это функция от вашего состояния (state
). Изменилось состояние — изменился UI.
UI = f(state)
Событие из внешнего мира (клик мыши, ответ от сервера) вызывает изменение состояния через setState
. Это, в свою очередь, запускает процесс обновления UI.
Внешне это выглядит как та самая реакция на изменения. Но дьявол, как всегда, в деталях. Нюанс заключается в том, КАК React решает обновить UI.
Когда вы вызываете setState
, React не бросается сломя голову перерисовывать DOM. Вместо этого он планирует обновление. Он как бы ставит себе пометку: «Ага, состояние изменилось. Скоро нужно будет обновить интерфейс». Если вы вызовете setState
несколько раз подряд в одном и том же обработчике, React окажется достаточно умён, чтобы объединить (сбатчить) их все в одно-единственное обновление.
Это фундаментальное отличие. В отличие от классической push-модели, где данные проталкиваются к подписчику, модель React можно назвать pull-based (основанной на запросе). React получает уведомление об изменении, но сам решает, когда запросить (pull) новое состояние и как наиболее эффективно обновить UI на его основе.
Так реактивен ли React?
Да, но он реализует свою, особую модель реактивности.
React реактивен в своей декларативной сути. Вы описываете, как должен выглядеть UI для определённого состояния, и доверяете React синхронизировать реальный DOM с этим описанием. В этом смысле он полностью соответствует широкому определению реактивности — UI автоматически реагирует на изменения данных.
Однако React не является «реактивным» в классическом, потоковом смысле, как RxJS. Он не предоставляет из коробки мощных инструментов для управления потоками событий. Его реактивность узко специализирована и нацелена на одну задачу: эффективное обновление дерева компонентов.
Пакетная обработка обновлений (batching) — это не то, что делает React «нереактивным». Как мы помним из примера с bufferTime
в RxJS, управление потоком и группировка событий — это часть инструментария реактивных систем. Это лишь подчёркивает, что React выбрал конкретную стратегию управления изменениями, поставив во главу угла производительность и предсказуемость рендеринга.
Итог
Разобравшись в моделях реактивности, мы можем сделать вывод: React — это реактивная библиотека, но её реактивность не та, что в классических push-библиотеках вроде RxJS.
React использует pull-based модель с планированием обновлений, идеально заточенную под задачи построения пользовательских интерфейсов. Именно поэтому для сложных сценариев управления асинхронными потоками (например, при работе с WebSockets или сложными анимациями) разработчики часто комбинируют React с библиотеками вроде RxJS, получая лучшее из двух миров.
Top comments (3)
Отличная статья, написанная простым и понятным языком. Доходчиво, с примерами. Буду рекомендовать.
Сперва вы путаете реактивность с программированием потоков данных.
Далее вы путаете паттерны PubSub и Observable
А в конце вообще зачем-то лезете внутрь компонента, обосновывая принадлежность парадигме особенностями внутренней реализации.
По вашей логике, если в pipe`е RxJS есть bufferTime, то поток перестает быть реактивным? Ну дурость же
То есть когда React заботится о производительности и батчит апдейты UI в ответ на часто меняющееся состояние - он перестает быть реактивным?
Суть же в том, что он изменение состояния переводит в изменения на UI. State/context обновился - приложение перерисовалось - изменения отобразились. Сами.
так никто и не контроллирует то как часто state/context меняются