Penulis materi, terjemahan yang kami terbitkan hari ini, mengatakan bahwa di sini ia ingin menunjukkan proses pengembangan aplikasi Bereaksi sederhana yang menggunakan RxJS. Menurutnya, dia bukan ahli dalam RxJS, karena dia terlibat dalam mempelajari perpustakaan ini dan tidak menolak bantuan orang-orang berpengetahuan. Tujuannya adalah untuk menarik perhatian audiens ke cara-cara alternatif untuk menciptakan aplikasi Bereaksi, untuk menginspirasi pembaca untuk melakukan penelitian independen. Materi ini tidak bisa disebut pengantar RxJS. Salah satu dari banyak cara untuk menggunakan perpustakaan ini dalam pengembangan Bereaksi akan ditampilkan di sini.

Bagaimana semuanya dimulai
Baru-baru ini,
klien saya menginspirasi saya untuk belajar menggunakan
RxJS untuk mengelola keadaan aplikasi Bereaksi. Ketika saya mengaudit kode aplikasi untuk klien ini, dia ingin tahu pendapat saya tentang bagaimana dia mengembangkan aplikasi, mengingat sebelumnya itu telah digunakan secara eksklusif negara bagian Bereaksi lokal. Proyek mencapai titik di mana tidak masuk akal untuk hanya mengandalkan React. Pertama, kami berbicara tentang menggunakan
Redux atau MobX sebagai cara yang lebih baik untuk mengelola keadaan aplikasi Anda. Klien saya membuat prototipe untuk masing-masing teknologi ini. Tapi dia tidak berhenti pada teknologi ini, membuat prototipe aplikasi Bereaksi yang menggunakan RxJS. Sejak saat itu, percakapan kami menjadi jauh lebih menarik.
Aplikasi yang dimaksud adalah platform perdagangan untuk cryptocurrency. Itu memiliki banyak widget yang datanya diperbarui secara real time. Para pengembang aplikasi ini, antara lain, harus menyelesaikan tugas-tugas sulit berikut:
- Kelola beberapa permintaan data (tidak sinkron).
- Pembaruan waktu-nyata dari sejumlah besar widget pada panel kontrol.
- Solusi untuk masalah widget dan konektivitas data, karena beberapa widget membutuhkan data tidak hanya dari beberapa sumber khusus, tetapi juga dari widget lain.
Akibatnya, kesulitan utama yang dihadapi pengembang tidak terkait dengan perpustakaan Bereaksi itu sendiri, dan selain itu, saya bisa membantu mereka dalam bidang ini. Masalah utama adalah membuat mekanisme internal sistem bekerja dengan benar, mekanisme yang menghubungkan data cryptocurrency dan elemen antarmuka yang dibuat oleh alat React. Di area inilah kemampuan RxJS sangat berguna, dan prototipe yang mereka tunjukkan terlihat sangat menjanjikan.
Menggunakan RxJS di Bereaksi
Misalkan kita memiliki aplikasi yang, setelah melakukan beberapa tindakan lokal, membuat permintaan ke
API pihak ketiga . Ini memungkinkan Anda untuk mencari berdasarkan artikel. Sebelum menjalankan permintaan, kita perlu mendapatkan teks yang digunakan untuk membentuk permintaan ini. Secara khusus, kami, menggunakan teks ini, membentuk URL untuk mengakses API. Berikut adalah kode komponen Bereaksi yang mengimplementasikan fungsi ini
import React from 'react'; const App = ({ query, onChangeQuery }) => ( <div> <h1>React with RxJS</h1> <input type="text" value={query} onChange={event => onChangeQuery(event.target.value)} /> <p>{`http://hn.algolia.com/api/v1/search?query=${query}`}</p> </div> ); export default App;
Komponen ini tidak memiliki sistem manajemen negara. Status untuk properti
query
tidak disimpan di mana pun, fungsi
onChangeQuery
juga tidak memperbarui status. Dalam pendekatan konvensional, komponen seperti itu dilengkapi dengan sistem manajemen negara bagian setempat. Ini terlihat seperti ini:
class App extends React.Component { constructor(props) { super(props); this.state = { query: '', }; } onChangeQuery = query => { this.setState({ query }); }; render() { return ( <div> <h1>React with RxJS</h1> <input type="text" value={this.state.query} onChange={event => this.onChangeQuery(event.target.value) } /> <p>{`http:
Namun, ini bukan pendekatan yang akan kita bicarakan di sini. Sebagai gantinya, kami ingin membuat sistem manajemen status aplikasi menggunakan RxJS. Mari kita lihat bagaimana melakukan ini menggunakan Komponen Orde Tinggi (HOC).
Jika Anda mau, Anda dapat menerapkan logika serupa di komponen
App
Anda, tetapi kemungkinan besar Anda, pada suatu saat dalam pekerjaan Anda pada aplikasi, memutuskan untuk merancang komponen seperti itu dalam bentuk
HOC yang cocok untuk digunakan kembali.
Bereaksi dan Komponen Top-Order RxJS
Kami akan mencari cara mengelola keadaan Bereaksi aplikasi menggunakan RxJS, menggunakan komponen orde tinggi untuk tujuan ini. Sebagai gantinya, orang dapat menerapkan
templat render props. Akibatnya, jika Anda tidak ingin membuat sendiri komponen tingkat tinggi untuk tujuan ini, Anda dapat menggunakan komponen tingkat tinggi yang dapat diamati.
mapPropsStream()
dengan
mapPropsStream()
dan
componentFromStream()
. Namun dalam panduan ini, kami akan melakukan semuanya sendiri.
import React from 'react'; const withObservableStream = (...) => Component => { return class extends React.Component { componentDidMount() {} componentWillUnmount() {} render() { return ( <Component {...this.props} {...this.state} /> ); } }; }; const App = ({ query, onChangeQuery }) => ( <div> <h1>React with RxJS</h1> <input type="text" value={query} onChange={event => onChangeQuery(event.target.value)} /> <p>{`http://hn.algolia.com/api/v1/search?query=${query}`}</p> </div> ); export default withObservableStream(...)(App);
Sementara komponen orde tinggi RxJS tidak melakukan tindakan apa pun. Ini hanya mentransfer status dan propertinya sendiri ke komponen input, yang rencananya akan diperluas dengan bantuannya. Seperti yang Anda lihat, pada akhirnya, komponen tingkat tinggi akan terlibat dalam mengelola keadaan Bereaksi. Namun, keadaan ini akan diperoleh dari aliran yang diamati. Sebelum kita mulai menerapkan HOC dan menggunakannya dengan komponen
App
, kita perlu menginstal RxJS:
npm install rxjs
Sekarang mari kita mulai menggunakan komponen tingkat tinggi dan menerapkan logikanya:
import React from 'react'; import { BehaviorSubject } from 'rxjs'; ... const App = ({ query, onChangeQuery }) => ( <div> <h1>React with RxJS</h1> <input type="text" value={query} onChange={event => onChangeQuery(event.target.value)} /> <p>{`http://hn.algolia.com/api/v1/search?query=${query}`}</p> </div> ); const query$ = new BehaviorSubject({ query: 'react' }); export default withObservableStream( query$, { onChangeQuery: value => query$.next({ query: value }), } )(App);
Komponen
App
itu sendiri tidak berubah. Kami baru saja menyampaikan dua argumen ke komponen pesanan yang lebih tinggi. Kami menggambarkannya:
- Objek yang diamati. Argumen
query
adalah objek yang dapat diamati yang memiliki nilai awal, tetapi, selain itu, mengembalikan, seiring waktu, nilai-nilai baru (karena ini adalah BehaviorSubject
). Siapa pun dapat berlangganan objek yang dapat diamati ini. Inilah yang dikatakan dokumentasi RxJS tentang objek bertipe BehaviorSubject
: "Salah satu opsi untuk objek Subject
adalah objek BehaviorSubject
yang menggunakan konsep" nilai saat ini ". Ia menyimpan nilai terakhir yang diteruskan ke pelanggannya, dan ketika pengamat baru berlangganan, ia segera menerima "nilai saat ini" dari BehaviorSubject
. Objek seperti itu sangat cocok untuk menyajikan data, bagian-bagian baru yang muncul seiring waktu. " - Sistem untuk mengeluarkan nilai baru untuk objek yang diamati (pemicu). Fungsi
onChangeQuery()
, melewati HOC ke komponen App
, adalah fungsi biasa yang meneruskan nilai berikutnya ke objek yang diamati. Fungsi ini ditransfer dalam objek, karena mungkin diperlukan untuk mentransfer ke komponen urutan tinggi beberapa fungsi yang melakukan tindakan tertentu dengan objek yang diamati.
Setelah membuat objek yang diamati dan berlangganan, aliran untuk permintaan harus bekerja. Namun, hingga sekarang, komponen tingkat tinggi itu sendiri tampak seperti kotak hitam bagi kami. Kami menyadarinya:
const withObservableStream = (observable, triggers) => Component => { return class extends React.Component { componentDidMount() { this.subscription = observable.subscribe(newState => this.setState({ ...newState }), ); } componentWillUnmount() { this.subscription.unsubscribe(); } render() { return ( <Component {...this.props} {...this.state} {...triggers} /> ); } }; };
Komponen tingkat tinggi menerima objek yang dapat diobservasi dan objek dengan pemicu (mungkin objek yang berisi fungsi ini dapat disebut beberapa istilah yang lebih sukses dari leksikon RxJS), diwakili dalam tanda tangan fungsi.
Pemicu hanya melewati HOC ke komponen input. Itulah sebabnya komponen
App
secara langsung menerima fungsi
onChangeQuery()
, yang langsung bekerja dengan objek yang diamati, meneruskan nilai baru ke sana.
Objek yang diamati menggunakan
componentDidMount()
metode siklus hidup untuk masuk dan metode
componentDidMount()
untuk berhenti berlangganan. Berhenti berlangganan diperlukan untuk mencegah
kebocoran memori . Dalam berlangganan objek yang diamati, fungsi hanya mengirim semua data yang masuk dari aliran ke toko negara Bereaksi lokal menggunakan perintah
this.setState()
.
Mari kita lakukan perubahan kecil pada komponen
App
, yang akan menghilangkan masalah yang terjadi jika komponen urutan tinggi tidak menetapkan nilai awal untuk properti
query
. Jika ini tidak dilakukan, maka, di awal pekerjaan, properti
query
akan berubah menjadi
undefined
. Berkat perubahan ini, properti ini mendapatkan nilai default.
const App = ({ query = '', onChangeQuery }) => ( <div> <h1>React with RxJS</h1> <input type="text" value={query} onChange={event => onChangeQuery(event.target.value)} /> <p>{`http://hn.algolia.com/api/v1/search?query=${query}`}</p> </div> );
Cara lain untuk mengatasi masalah ini adalah dengan menetapkan keadaan awal untuk
query
dalam komponen tingkat tinggi:
const withObservableStream = ( observable, triggers, initialState, ) => Component => { return class extends React.Component { constructor(props) { super(props); this.state = { ...initialState, }; } componentDidMount() { this.subscription = observable.subscribe(newState => this.setState({ ...newState }), ); } componentWillUnmount() { this.subscription.unsubscribe(); } render() { return ( <Component {...this.props} {...this.state} {...triggers} /> ); } }; }; const App = ({ query, onChangeQuery }) => ( ... ); export default withObservableStream( query$, { onChangeQuery: value => query$.next({ query: value }), }, { query: '', } )(App);
Jika Anda mencoba aplikasi ini sekarang, kotak input harus berfungsi seperti yang diharapkan. Komponen
App
menerima dari HOC, dalam bentuk properti, hanya status
query
dan fungsi
onChangeQuery
untuk mengubah status.
Mengambil dan mengubah keadaan terjadi melalui objek yang dapat diamati RxJS, meskipun fakta bahwa toko keadaan Bereaksi internal digunakan di dalam komponen tingkat tinggi. Saya tidak dapat menemukan solusi yang jelas untuk masalah streaming data dari berlangganan objek yang diamati langsung ke properti komponen yang diperluas (
App
). Itu sebabnya saya harus menggunakan keadaan lokal Bereaksi dalam bentuk lapisan menengah, yang, selain itu, nyaman dalam hal apa yang menyebabkan rendering ulang. Jika Anda tahu cara lain untuk mencapai tujuan yang sama - Anda dapat membagikannya di komentar.
Menggabungkan Objek yang Diamati dalam Bereaksi
Mari kita buat aliran nilai kedua, yang, seperti properti
query
, dapat digunakan dalam komponen
App
. Nanti kita akan menggunakan kedua nilai tersebut, bekerja dengannya menggunakan objek lain yang bisa diamati.
const SUBJECT = { POPULARITY: 'search', DATE: 'search_by_date', }; const App = ({ query = '', subject, onChangeQuery, onSelectSubject, }) => ( <div> <h1>React with RxJS</h1> <input type="text" value={query} onChange={event => onChangeQuery(event.target.value)} /> <div> {Object.values(SUBJECT).map(value => ( <button key={value} onClick={() => onSelectSubject(value)} type="button" > {value} </button> ))} </div> <p>{`http://hn.algolia.com/api/v1/${subject}?query=${query}`}</p> </div> );
Seperti yang Anda lihat, parameter
subject
dapat digunakan untuk menyaring permintaan saat membuat URL yang digunakan untuk mengakses API. Yaitu, materi dapat dicari berdasarkan popularitasnya atau pada tanggal publikasi. Selanjutnya, buat objek lain yang bisa diamati yang dapat digunakan untuk mengubah parameter
subject
. Dapat diamati ini dapat digunakan untuk mengaitkan komponen
App
dengan komponen urutan yang lebih tinggi. Jika tidak, properti yang diteruskan ke komponen
App
tidak akan berfungsi.
import React from 'react'; import { BehaviorSubject, combineLatest } from 'rxjs/index'; ... const query$ = new BehaviorSubject({ query: 'react' }); const subject$ = new BehaviorSubject(SUBJECT.POPULARITY); export default withObservableStream( combineLatest(subject$, query$, (subject, query) => ({ subject, query, })), { onChangeQuery: value => query$.next({ query: value }), onSelectSubject: subject => subject$.next(subject), }, )(App);
Pemicu
onSelectSubject()
bukanlah hal baru. Melalui tombol, dapat digunakan untuk beralih di antara dua status
subject
. Tetapi objek yang diamati dipindahkan ke komponen tingkat tinggi adalah sesuatu yang baru. Ini menggunakan fungsi
combineLatest()
dari RxJS untuk menggabungkan nilai-nilai yang dikembalikan terakhir dari dua (atau lebih) stream yang diamati. Setelah berlangganan objek yang diamati, jika salah satu nilai (
query
atau
subject
) diubah, pelanggan akan menerima kedua nilai tersebut.
Sebuah pelengkap mekanisme yang diimplementasikan oleh fungsi
combineLatest()
adalah argumen terakhirnya. Di sini Anda dapat mengatur urutan pengembalian nilai yang dihasilkan oleh objek yang diamati. Dalam kasus kami, kami membutuhkan mereka untuk diwakili sebagai objek. Ini akan memungkinkan, seperti sebelumnya, menghancurkannya dalam komponen tingkat tinggi dan menuliskannya ke status Bereaksi lokal. Karena kita sudah memiliki struktur yang diperlukan, kita dapat menghilangkan langkah membungkus objek dari objek
query
diamati.
... const query$ = new BehaviorSubject('react'); const subject$ = new BehaviorSubject(SUBJECT.POPULARITY); export default withObservableStream( combineLatest(subject$, query$, (subject, query) => ({ subject, query, })), { onChangeQuery: value => query$.next(value), onSelectSubject: subject => subject$.next(subject), }, )(App);
Objek sumber,
{ query: '', subject: 'search' }
, serta semua objek lain yang dikembalikan oleh aliran gabungan dari objek yang diamati, cocok untuk merusaknya dalam komponen tingkat tinggi dan untuk menulis nilai yang sesuai ke keadaan Bereaksi lokal. Setelah memperbarui keadaan, seperti sebelumnya, rendering dilakukan. Ketika Anda meluncurkan aplikasi yang diperbarui, Anda harus dapat mengubah kedua nilai menggunakan bidang input dan tombol. Nilai yang diubah memengaruhi URL yang digunakan untuk mengakses API. Sekalipun hanya satu dari nilai-nilai ini yang berubah, nilai yang lain mempertahankan status terakhirnya, karena fungsi
combineLatest()
selalu menggabungkan nilai terbaru yang dikeluarkan dari aliran yang diamati.
Aksioma dan RxJS dalam Bereaksi
Sekarang di sistem kami, URL untuk mengakses API dibangun berdasarkan dua nilai dari objek yang dapat diamati yang dikombinasikan, yang mencakup dua objek yang dapat diamati lainnya. Di bagian ini, kami akan menggunakan URL untuk memuat data dari API. Anda mungkin dapat menggunakan sistem
React loading data , tetapi ketika menggunakan objek yang dapat diamati RxJS, Anda perlu menambahkan aliran lain yang dapat diamati ke aplikasi kami.
Sebelum kita mulai bekerja pada objek yang diamati berikutnya, instal
aksioma . Ini adalah perpustakaan yang akan kita gunakan untuk memuat data dari stream ke dalam program.
npm install axios
Sekarang bayangkan bahwa kita memiliki berbagai artikel yang harus dihasilkan oleh komponen
App
. Di sini, sebagai nilai parameter default yang sesuai, kami menggunakan array kosong, melakukan hal yang sama seperti yang kami lakukan dengan parameter lainnya.
... const App = ({ query = '', subject, stories = [], onChangeQuery, onSelectSubject, }) => ( <div> ... <p>{`http://hn.algolia.com/api/v1/${subject}?query=${query}`}</p> <ul> {stories.map(story => ( <li key={story.objectID}> <a href={story.url || story.story_url}> {story.title || story.story_title} </a> </li> ))} </ul> </div> );
Untuk setiap artikel dalam daftar, nilai mundur digunakan karena fakta bahwa API yang kami akses tidak seragam. Sekarang - yang paling menarik - implementasi objek baru yang dapat diamati yang bertanggung jawab untuk memuat data ke dalam aplikasi Bereaksi yang akan memvisualisasikannya.
import React from 'react'; import axios from 'axios'; import { BehaviorSubject, combineLatest } from 'rxjs'; import { flatMap, map } from 'rxjs/operators'; ... const query$ = new BehaviorSubject('react'); const subject$ = new BehaviorSubject(SUBJECT.POPULARITY); const fetch$ = combineLatest(subject$, query$).pipe( flatMap(([subject, query]) => axios(`http://hn.algolia.com/api/v1/${subject}?query=${query}`), ), map(result => result.data.hits), ); ...
Objek baru yang dapat diamati adalah, sekali lagi, kombinasi dari
subject
dan
query
dapat diamati, karena, untuk membangun URL yang dengannya kita akan mengakses API untuk memuat data, kita membutuhkan kedua nilai tersebut. Dalam metode
pipe()
dari objek yang diamati, kita dapat menggunakan apa yang disebut "operator RxJS" untuk melakukan tindakan tertentu dengan nilai-nilai. Dalam kasus ini, kami memetakan dua nilai yang ditempatkan dalam kueri, yang digunakan aksioma untuk mendapatkan hasilnya. Di sini kita menggunakan operator
flatMap()
dan bukan
map()
untuk mengakses hasil dari janji yang berhasil diselesaikan, dan bukan ke janji yang dikembalikan itu sendiri. Akibatnya, setelah berlangganan objek baru yang dapat diamati ini, setiap kali
subject
baru atau nilai
query
dimasukkan ke dalam sistem dari objek lain yang dapat diamati,
query
baru dijalankan, dan hasilnya berada dalam fungsi berlangganan.
Sekarang, sekali lagi, kami dapat memberikan komponen baru yang dapat diamati ke komponen tingkat tinggi. Kami memiliki argumen terakhir untuk fungsi
combineLatest()
kami miliki, ini memungkinkan untuk memetakannya langsung ke properti yang disebut
stories
. Pada akhirnya, ini menunjukkan bagaimana data ini sudah digunakan dalam komponen
App
.
export default withObservableStream( combineLatest( subject$, query$, fetch$, (subject, query, stories) => ({ subject, query, stories, }), ), { onChangeQuery: value => query$.next(value), onSelectSubject: subject => subject$.next(subject), }, )(App)
Tidak ada pemicu di sini, karena objek yang diamati diaktifkan secara tidak langsung oleh dua aliran yang diamati lainnya. Setiap kali nilai dalam bidang input (
query
) diubah atau tombol (
subject
) diklik, ini memengaruhi objek
fetch
diamati, yang berisi nilai terbaru dari kedua aliran.
Namun, mungkin kita tidak perlu bahwa setiap kali kita mengubah nilai di bidang input, ini akan memengaruhi objek
fetch
diamati. Selain itu, kami tidak ingin
fetch
terpengaruh jika nilainya adalah string kosong. Itulah sebabnya kami dapat memperluas objek
query
dapat diamati menggunakan pernyataan
debounce
, yang menghilangkan terlalu sering perubahan kueri. Yaitu, berkat mekanisme ini, acara baru diterima hanya setelah waktu yang telah ditentukan setelah acara sebelumnya. Selain itu, kami menggunakan operator
filter
sini, yang memfilter peristiwa aliran jika string
query
kosong.
import React from 'react'; import axios from 'axios'; import { BehaviorSubject, combineLatest, timer } from 'rxjs'; import { flatMap, map, debounce, filter } from 'rxjs/operators'; ... const queryForFetch$ = query$.pipe( debounce(() => timer(1000)), filter(query => query !== ''), ); const fetch$ = combineLatest(subject$, queryForFetch$).pipe( flatMap(([subject, query]) => axios(`http://hn.algolia.com/api/v1/${subject}?query=${query}`), ), map(result => result.data.hits), ); ...
Pernyataan
debounce
melakukan pekerjaan memasukkan data ke dalam bidang. Namun, ketika sebuah tombol mengklik pada nilai
subject
, permintaan tersebut harus segera dieksekusi.
Sekarang, nilai awal untuk
query
dan
subject
, yang kita lihat ketika komponen
App
ditampilkan untuk pertama kalinya, tidak sama dengan yang diperoleh dari nilai awal objek yang diamati:
const query$ = new BehaviorSubject('react'); const subject$ = new BehaviorSubject(SUBJECT.POPULARITY);
subject
ditulis dalam
subject
, string kosong dalam
query
. Ini disebabkan oleh fakta bahwa nilai-nilai inilah yang kami berikan sebagai parameter default untuk merestrukturisasi fungsi komponen
App
dalam tanda tangan. Alasan untuk ini adalah karena kita perlu menunggu permintaan awal dieksekusi oleh objek
fetch
dapat diamati. Karena saya tidak tahu persis bagaimana cara segera mendapatkan nilai dari
query
dapat diamati dan objek
subject
dalam komponen tingkat tinggi untuk menulisnya ke keadaan lokal, saya memutuskan untuk mengatur keadaan awal untuk komponen tingkat tinggi lagi.
const withObservableStream = ( observable, triggers, initialState, ) => Component => { return class extends React.Component { constructor(props) { super(props); this.state = { ...initialState, }; } componentDidMount() { this.subscription = observable.subscribe(newState => this.setState({ ...newState }), ); } componentWillUnmount() { this.subscription.unsubscribe(); } render() { return ( <Component {...this.props} {...this.state} {...triggers} /> ); } }; };
Sekarang keadaan awal dapat diberikan ke komponen tingkat tinggi sebagai argumen ketiga. Di masa mendatang, kita dapat menghapus pengaturan default untuk komponen
App
.
... const App = ({ query, subject, stories, onChangeQuery, onSelectSubject, }) => ( ... ); export default withObservableStream( combineLatest( subject$, query$, fetch$, (subject, query, stories) => ({ subject, query, stories, }), ), { onSelectSubject: subject => subject$.next(subject), onChangeQuery: value => query$.next(value), }, { query: 'react', subject: SUBJECT.POPULARITY, stories: [], }, )(App);
Yang membuat saya khawatir saat ini adalah bahwa keadaan awal juga diatur dalam deklarasi objek yang diamati,
query$
dan
subject$
. Pendekatan ini rawan kesalahan, karena inisialisasi objek yang diamati dan keadaan awal komponen tingkat tinggi memiliki nilai yang sama. Saya akan lebih menyukainya jika, sebaliknya, nilai-nilai awal diekstraksi dari objek yang diamati dalam komponen tingkat tinggi untuk mengatur keadaan awal. Mungkin salah satu pembaca materi ini akan dapat berbagi saran dalam komentar tentang cara melakukan ini.
Kode untuk proyek perangkat lunak yang kami lakukan di sini dapat ditemukan di
sini .
Ringkasan
Tujuan utama artikel ini adalah untuk menunjukkan pendekatan alternatif untuk mengembangkan aplikasi Bereaksi menggunakan RxJS. Kami harap dia memberi Anda makanan untuk dipikirkan. Terkadang Redux dan MobX tidak diperlukan, tetapi mungkin dalam situasi seperti itu, RxJS akan sesuai dengan proyek tertentu.
Pembaca yang budiman! Apakah Anda menggunakan RxJS ketika mengembangkan Bereaksi aplikasi?
