Загляните в мой блог о том, как создавать веб-игры здесь.
RxJS, пожалуй, самая крутая библиотека для решения сложных, асинхронных и загадочных проблем в веб-разработке с легкостью и совершенство. Приложения RxJS достаточно разнообразны, чтобы классифицировать его как достаточно универсальную библиотеку. Учитывая то ограниченное время, которое я потратил на использование RxJS, я уже нашел несколько классических приложений RxJS в дикой природе.
Однако, если честно, для новичка , немного сложно удержаться, и вот почему.
Что такое RxJS?
Знание, что такое RxJS и как он помогает, является наиболее важной частью обучения. Давайте посмотрим на некоторые определения, полученные от Google.
RxJS - это библиотека для реактивного программирования с использованием Observables, чтобы упростить составление асинхронного кода или кода на основе обратного вызова.
Реактивное программирование - это просто другой способ создания программных приложений. По сути, ваше программное обеспечение создано для «реакции» на происходящие изменения, а не для типичного способа написания программного обеспечения, при котором мы явно пишем код (также известный как «императивное» программирование) для обработки этих изменений.
Одна из самых привлекательных особенностей интеграции RxJS в вашу базу кода заключается в том, что чем больше вы его используете, тем больше вы можете с ним делать.
Все это звучит потрясающе, однако у нас до сих пор нет четкого представления о том, зачем нам вообще нужен RxJS.
Что на самом деле делает RxJS?
Любое функциональное приложение во многом зависит от таких событий, как источник события и то, что с ним нужно делать.
RxJS использует Observables для наблюдения (очевидно) за этими событиями и выполнения операций (операторов RxJS) для получения значимых результатов.
Наблюдаемые просто, их можно рассматривать как массив или поток, которые заполняются с течением времени. Мы получаем Операторы, чтобы получить значимую функциональность из этого потока.
Теперь у всех событий в моем приложении будет соответствующий наблюдатель, который точно знает, как обрабатывать соответствующее событие. Мы перешли от традиционных к более реактивным отношениям между данными и представлением.
Почему мне должен быть интересен RxJS?
Асинхронный поток данных
В React обычно мы используем flux, Redux, Mobx, MST для обработки наш поток данных. Однако ни одна из этих библиотек не решает проблему асинхронности. В Redux у нас есть промежуточное ПО для обработки асинхронной логики, например Thunk.
Однако сам Thunk связан с некоторыми проблемами, такими как несоответствие хранимых данных и данных, отправленных на сервер. Кроме того, это усложняет наш редуктор из-за слишком большого количества переменных для реализации простой общей логики.
Вышеупомянутые проблемы подробно описаны в этих блогах.
RxJS имеет элегантный API для эффективной обработки всей асинхронной логики, который мы рассмотрим позже. Здесь также довольно легко реализовать обработку ошибок и отмену запроса.
Делайте больше с меньшим количеством кода
RxJS по своей природе представляет собой компактную библиотеку для реактивного программирования.
Довольно сложные функции могут быть легко реализованы с помощью Observables и Операторы .
Всегда есть оператор. Каким бы ни был вариант использования.
Идея никогда больше не писать сложную логику звучит привлекательно.
Новая функция с минимальным рефакторингом
Добавление новых функций в кодовую базу RxJS обычно включает простое добавление, удаление или перестановку операторов для достижения желаемого результата. Мы увидим, как это будет выглядеть через некоторое время.
Практический пример RxJS
Не секрет, что у RxJS довольно крутая кривая обучения, и его использование требует практики.
Теперь, похоже, самое подходящее время упомянуть, как легко сделать ошибку при использовании Observables. Если вы только начинаете с RxJS, скорее всего, вы делаете некоторые типичные ошибки, которые обычно допускают разработчики. Не волнуйтесь, в следующей статье все это объясняется.
Лучший способ изучить RxJS - использовать соответствующий пример. Представляем…
Реактивные войны
ReactiveWars - это простое одностраничное приложение React, которое использует RxJS для обработки потока данных. Мы будем использовать StarWars API для получения результатов поискового запроса от пользователя и отображения результатов.
Полный код оформления заказа здесь
Цель
Возьмем 3 типа сущностей, которые мы можем запросить из SWAPI.
1 ›Люди
2› Планеты
3 ›Транспорт
Позвоним эти параметры как Настройки, которые пользователь может выбрать или отменить.
Все три настройки доступны в виде кнопок в пользовательском интерфейсе для выбора и отмените выбор.
У нас также есть кнопка поиска для получения результатов и поле ввода.
Мы должны убедиться, что не выполняются избыточные вызовы.
Окончательный пользовательский интерфейс выглядит примерно так.
Давай поиграем
Теперь мы готовы углубиться в код.
Я создал стандартный проект create-response-app , добавил RxJS как зависимость и реализовал пользовательский интерфейс, показанный выше. Кнопки заполняются путем сопоставления массива состояний предпочтений, показанного ниже.
MakeCallStream
Давайте обработаем нажатия кнопки Поиск с помощью RxJS Темы.
Мы собираемся создать новый поток для кнопки поиска и отправлять события в этот поток при каждом нажатии кнопки.
Здесь мы используем flatMap из RxJS.
Нам нужно пройти через массив this.state.prefStatus
и выполнить выбранные вызовы только для этих настроек.
Код: -
InputBoxStream
Давайте создадим inputStream, чтобы принять изменения в поле ввода и установить его в состояние.
Он сделал что-то вроде этого.
PrefButtonStream
Давайте рассмотрим наши требования здесь
1 ›Нам нужно выбрать / отменить выбор кнопки при нажатии.
2 ›Нам также необходимо выполнить вызовы API, чтобы обновить наш список в соответствии с выбранными настройками. (Наш makeCallStream выше уже позаботился об этом).
3 ›Если нажатая кнопка является единственной выбранной кнопкой, мы хотим пропустить это событие, потому что всегда должна быть выбрана хотя бы одна кнопка pref.
Теперь давайте реализуем это в RxJS
GetDataStream
Наши требования следующие
1 ›Нам нужно получать данные для каждого отдельного поискового запроса.
2 ›Мы должны следить за тем, чтобы не производились избыточные вызовы. Для этого мы используем оператор independentUntilChanged.
3 ›Нам нужно обрабатывать ошибки в вызове API. Мы используем catchError и передаем пустой Observable для обработки случая ошибки здесь.
4 ›Мы увидим, как передавать данные по нисходящему потоку для использования на более позднем этапе с помощью оператора of .
Это выглядит примерно так
И вот оно, мы выполнили все, что обещали. Вот как это выглядит.
Полный код оформления заказа для этого здесь.
Дополнительные функции / изменения
В этом разделе показано, как легко добавлять новые функции к существующей кодовой базе.
1 ›Допустим, мы хотим ограничить частоту одновременных вызовов, когда пользователь выбрал несколько настроек и выполнил поиск. Поэтому нам нужно обеспечить, чтобы все вызовы происходили только с интервалом 500 мс.
Этого можно добиться с помощью простого рефакторинга, описанного ниже в getDataStream, с использованием оператора debounceTime.
getDataStream.pipe( debounceTime(500), // debounce concurrent calls by 500ms flatMap(val => { return fetch(`https://swapi.co/') }), ...
2 ›Предположим, мы хотим выполнять вызовы API при каждом изменении текста, но опять же, мы не должны делать никаких избыточных вызовов.
Это снова достижимо с помощью простого рефакторинга в inputStream, где мы вызываем makeCallStream.
inputStream.subscribe(val => { this.setState({ searchText: val.value }, () => makeCallStream.next({ value: this.state.searchText }); });
3 ›Наконец, предположим, что нам нужно объединить несколько вызовов в цепочку. Например, когда пользователь нажимает на элемент люди, мы должны выбрать его автомобиль, а затем распечатать старшего из пилотов (людей), которые когда-либо использовали этот автомобиль.
Мы можем связать эти запросы и использовать flatMap для достижения окончательного результата.
vehicleDeepStream.pipe( flatMap(person => fetch('vehicle/of/person')), flatMap(vehicle => fetch('all/pilots/of/vehicle')), flatMap(allPilots => eldest(allPilots)), ).subscribe(eldest => console.log(eldest))
А что, если бы мы хотели ограничить API человека на 100 мс, а API транспортного средства - на 200 мс?
Опять же, это требует простого рефакторинга.
vehicleDeepStream.pipe( debounceTime(100), flatMap(person => fetch('vehicle/of/person')), debounceTime(200), flatMap(vehicle => fetch('all/pilots/vehicle')), flatMap(allPilots => eldest(allPilots)), ).subscribe(eldest => console.log(eldest))
Заключение
То, что мы рассмотрели в этой статье, минимально по сравнению с универсальностью RxJS.
Мы можем использовать RxJS для обработки синхронизации, а также асинхронная логика в нашем приложении, поток данных, анимация, внутренняя служебная программа функции и многое другое. Этот список можно продолжить.
Более того, RxJS действительно начинает сиять, когда вы его берете в руки.
Некоторые крупные компании, использующие RxJS, включают
Microsoft, Netflix, GitHub, SoundCloud, Trello, Treehouse, Slack and more
Узнать больше здесь.
Надеюсь, это поможет вам начать работу с RxJS для React.
Ознакомьтесь с моим redux-observable пример приложения для ReactNative.