How to use RxJS with React Hooks

LogRocket
28 Oct 201920:02

Summary

TLDREn este video tutorial, exploramos el uso de RxJS en combinación con React Hooks, asumiendo un conocimiento previo de React. Comenzamos con una introducción a RxJS y los Observables, destacando su capacidad para manejar múltiples valores a lo largo del tiempo, a diferencia de las promesas. Después, nos sumergimos en ejemplos prácticos usando CodeSandbox, donde implementamos Observables básicos y los integramos en componentes de React, primero con clases y luego con Hooks para una sintaxis más limpia y menos verbosa. Finalmente, aplicamos estos conceptos en un ejemplo real de búsqueda de sugerencias usando la API de Pokémon, demostrando la potencia y eficiencia de RxJS para el manejo de eventos y datos asincrónicos en aplicaciones React.

Takeaways

  • 😀 RxJS es una biblioteca de extensiones reactivas para JavaScript que permite la programación reactiva utilizando observables.
  • 🤔 Los observables son colecciones invocables de futuros valores o eventos que pueden enviar múltiples valores a lo largo del tiempo, a diferencia de las promesas que solo se resuelven una vez.
  • 🚀 RxJS permite la ejecución perezosa, lo que significa que las operaciones complejas no se ejecutan hasta que se suscriben a ellos.
  • ✨ Los operadores de RxJS permiten transformar y componer observables, lo que brinda una forma poderosa de procesar datos asincrónicos.
  • 🎣 Puedes utilizar RxJS con React Hooks para manejar flujos de datos asincrónicos y reactivos de una manera más limpia y declarativa.
  • 🧩 Los Hooks personalizados, como useObservable, pueden encapsular la lógica de suscripción y cancelación, lo que facilita el uso de observables en componentes de React.
  • 🔍 RxJS es especialmente útil para implementar casos de uso como búsquedas con sugerencias automáticas, donde se necesita manejar flujos de datos asincrónicos y aplicar operadores como debounce y distinctUntilChanged.
  • 🧪 Los Subjects de RxJS actúan como observables ejecutables, lo que permite compartir datos y eventos entre múltiples observables.
  • 🌍 Existen bibliotecas de terceros, como rxjs-hooks, que simplifican aún más el uso de RxJS con React Hooks.
  • 📚 RxJS es una biblioteca madura y ampliamente utilizada, con una gran comunidad y recursos disponibles para aprender y profundizar en su uso.

Transcripts

play00:00

however now the going team here and

play00:02

today I want to talk about how to use

play00:04

rxjs together with react hooks this

play00:08

video assumes that you are familiar with

play00:10

react and feel at home with react hooks

play00:12

so let's start by talking about what is

play00:14

rxjs and observables rxjs is a reactive

play00:18

extensions library for JavaScript as it

play00:20

says on the website and it's essentially

play00:22

a library for reactive programming using

play00:24

observables if you are not familiar with

play00:27

all of those concepts don't worry we're

play00:28

gonna go over them in a minute now let's

play00:32

start by talking about what is

play00:33

observables observables is basically an

play00:36

idea of an in vocable collection of

play00:39

future values of events I like to think

play00:42

about it this way so you have a promise

play00:43

and promise can only resolve one time

play00:46

right so it can either be a rejection or

play00:48

fulfillment but it will only resolve

play00:51

once while observable have an option of

play00:54

actually sending more than one value or

play00:57

event over time and this is essentially

play00:59

what you have to know about observables

play01:01

right the cool thing is that observables

play01:04

are actually in process of getting

play01:05

standardized into ACMA scripts there's

play01:07

been some problems with the proposal and

play01:10

it's still stuck on a stage one but it's

play01:12

coming to the language sooner or later

play01:15

at least that's my opinion and there's

play01:17

like a lot of effort put in the proposal

play01:19

so if you're curious the links to all

play01:21

the websites that I'm showing are going

play01:23

to be in a video description if in case

play01:24

you want to check them out for yourself

play01:26

alright now that we got the basics out

play01:29

of the way let's just jump right into

play01:31

code sandbox and see how exactly do you

play01:34

use rxjs and observables in a standalone

play01:38

fashion right so I'm just going to

play01:39

create a new vanilla sandbox I'm gonna

play01:41

add our XJS here as a dependency and

play01:43

that's basically all we need so what

play01:46

we're gonna do is we're gonna implement

play01:47

a very basic observable that will show

play01:51

us the numbers right so I'm not I'm not

play01:53

even gonna render it in this case I'm

play01:55

just gonna import from from rxjs

play02:00

so this is the from operator allows you

play02:02

to create observables from other

play02:05

iterable things or in some other special

play02:09

cases like promises in this case we're

play02:11

going to create a new

play02:13

numbers observable right and we're gonna

play02:16

say from array and in this case is just

play02:18

gonna be numbers so very straightforward

play02:21

nothing super fancy here right now let's

play02:24

say we want to actually do something to

play02:26

those numbers right so let's say we want

play02:29

to have squared numbers so how would you

play02:31

go about that squared numbers is going

play02:34

to be equal now we're going to take our

play02:36

observable and pipe its to operator so

play02:41

called operators which is basically

play02:42

modification functions which is

play02:44

something we can import from rxjs slash

play02:50

operators right in this case let's say

play02:53

we also we are saying we want to squared

play02:54

numbers which means that we want to

play02:56

square them which means we need the map

play02:57

function right so we're gonna pipe it to

play02:59

the map function that takes in a value

play03:03

and then does something to this value

play03:05

since we're squaring them we're gonna do

play03:07

exactly that so it's gonna be squared

play03:09

numbers right now if I try to

play03:11

console.log this squared numbers we're

play03:15

actually not gonna see the numbers

play03:17

themselves but we're gonna see the

play03:18

observable this is exactly what I was

play03:20

talking about before so it's a primitive

play03:21

right now what do we do actually if we

play03:25

want to see the real numbers we have to

play03:27

subscribe to it so this is one of the

play03:29

things of the observables to consume it

play03:31

you subscribe to it right so we got the

play03:32

result and if we console.log the result

play03:37

in this case we're gonna see that we

play03:39

print out the values one at a time so

play03:42

this happens for each value right the

play03:45

whole pipe happens for each value it's

play03:48

also lazy this is like one of the

play03:50

strengths of the observables they are

play03:52

executed lazily which means that okay

play03:54

say we have this range say we want to

play03:57

filter so we're going to auto import the

play03:59

filter function from the observable

play04:02

operators and say we want to say okay we

play04:04

want all the values that are larger than

play04:07

two what the laziness means is that

play04:10

actually the square the map that we

play04:13

define here it's only going to be

play04:14

executed after the filter is done so

play04:17

it's not gonna do any work whatever the

play04:20

complex pipelines you have there after

play04:22

the filter on any of the values coming

play04:24

before that this is like one of the

play04:26

bigger

play04:26

things about the observables right this

play04:30

is the basic usage so it's very

play04:32

straightforward worth noting that

play04:33

basically once you subscribe you get the

play04:36

subscription objects and you can

play04:40

unsubscribe from it so say if we want

play04:43

the for some reason to dispose of it

play04:46

right so we're going to console.log the

play04:48

first number we get and then we're gonna

play04:49

say subscription dot unsubscribe which

play04:52

will result in us just getting the first

play04:54

number and then everything else again is

play04:56

going to be discarded and never executed

play04:58

because observables are lazy and if

play05:00

nobody is subscribed to them they're not

play05:02

gonna do anything okay so this is the

play05:04

basic case now let's talk about the

play05:08

usage of observables in react without

play05:11

hooks so like the you know the the old

play05:13

standard way of doing that so we gots

play05:15

the code sandbox for react here once

play05:18

again I'm gonna import rxjs and we won't

play05:22

actually be needing any new files here

play05:23

so I'm gonna just you know copy this

play05:25

code we have over here because let's

play05:28

just start with implementing the exactly

play05:30

same thing but rendering the numbers

play05:33

inside of our react app right so how do

play05:38

you go about that well in this case we

play05:41

got the function components so let's

play05:44

make it into a class stands react start

play05:48

components right and this is gonna be

play05:51

our render method and we need another

play05:54

closing bracket there we go okay so now

play05:57

we got the class rendering as expected

play05:59

so what do we have to do to work with

play06:01

observables well first of all we need to

play06:03

define a column sorry the estate because

play06:07

it will need to be updated so the our

play06:12

component has to react to the changes

play06:14

right I forgot the super over here and

play06:17

we're gonna say okay that current number

play06:19

is one for example I guess let's start

play06:23

with zero because we don't have zero in

play06:25

our numbers array right and okay so we

play06:29

define that we set the state so now we

play06:31

can actually render it let's create a

play06:34

simple render here current number is

play06:39

this states that come on state current

play06:43

number right so we got that now we need

play06:47

to actually listen to the observable so

play06:49

we obviously cannot just subscribe to it

play06:52

and never unsubscribe because they will

play06:54

lead to memory leaks and problems in the

play06:56

long term with the amp so that means

play06:59

that we have to handle two things we

play07:01

have to handle component did mount right

play07:04

so this is where we set up our

play07:05

subscription this is exactly what we

play07:07

want to do but instead of saying that

play07:10

let's subscription we're gonna say this

play07:11

subscription and store it on the

play07:13

component itself right and in this case

play07:17

we're not gonna unsubscribe here and

play07:19

instead of using console.log we're gonna

play07:21

set States to our current number result

play07:25

right so this is exactly what we want to

play07:27

do now this works we get our final

play07:31

number because the numbers are being set

play07:32

dynamically but the problem is that on

play07:34

every mount on every refresh on every

play07:37

page change for example this is gonna

play07:39

result in more and more listeners set

play07:42

right which is not something we want

play07:44

which means that we need to clean up

play07:45

after us so on component will unmount

play07:48

we're gonna say this subscription and

play07:50

subscribe not too bad right so it's it

play07:53

works but yeah it's not far from being

play07:57

perfect essentially right now let's make

play07:59

it more interesting so let's say we want

play08:01

to see the numbers dynamically here in G

play08:03

right so let's how do we do that well

play08:06

there is a delay function that that is

play08:09

one of the operators for HGS and let's

play08:12

say we want to see one number per second

play08:14

so if we do that you see it actually

play08:16

kind of works but it still shows us only

play08:18

the last number this is because the

play08:21

delay is applied to all of the values

play08:23

instantly right so we go through all of

play08:25

them and you set the delay for one

play08:27

second for each and while the delay is

play08:29

there it still applies it to every value

play08:32

which is not something we want to see so

play08:35

what we actually want to do is again one

play08:37

of the probably the most powerful

play08:39

features of our XJS is we want to merge

play08:41

map the value into a new observable that

play08:44

will be delayed so there's the merge map

play08:47

operator merge map right

play08:50

it takes the function that

play08:52

the value and returns something new so

play08:54

in this case we're gonna say that we

play08:56

create a new observable from an array

play08:58

that only contains our value so we're

play09:00

gonna pipe it into one second delay

play09:02

multiplied by the number so that the

play09:05

delay is increased every time right and

play09:07

as you can see here we finally have our

play09:10

result that is working so we actually

play09:11

can see on the page reload that the

play09:14

number increases over time right so okay

play09:16

we're skipping the value one and two in

play09:18

this case so the we're getting three

play09:20

four and five seconds delays but it

play09:22

works as expected right so pretty

play09:24

straightforward still that is a lot of

play09:27

code to set this up and if you would

play09:28

have to do that in every component that

play09:30

consumes observables is just not as

play09:32

convenient right so let's rewrite that

play09:35

to work with hooks first of all we're

play09:38

gonna change this into a function again

play09:40

right so we're going to have a function

play09:41

f that is this and it returns the

play09:46

renderer as before so this works

play09:48

perfectly fine this doesn't have to be a

play09:50

render anymore so we can just return

play09:52

this right let me just format this real

play09:54

quick now instead of this state and

play09:57

constructor we're gonna have you state

play10:00

hook so I'm gonna just use the current

play10:03

number and then we're gonna have set

play10:04

current number and this is gonna be use

play10:08

stage which is going to be equal to zero

play10:11

by default right so there's there we go

play10:13

there's our state now we have to figure

play10:15

out how to do the whole subscription on

play10:17

subscription again if you're familiar

play10:19

with the react hoax you know that there

play10:21

is a used effect hook that basically

play10:23

does exactly what we want

play10:25

so we're gonna do this and there's our

play10:28

use effect hook in this case when we

play10:30

want to run it once I'm gonna provide an

play10:32

MTA dependencies array because nothing

play10:35

really changes in this app right okay so

play10:37

we got the subscription setup well we

play10:39

have to change something we have to

play10:41

actually return the dispose function and

play10:43

we have to set the subscription to the

play10:45

local variable which makes it a bit

play10:47

cleaner and okay in this case we should

play10:50

use the current number over here and

play10:51

instead of this set state we're gonna

play10:53

use set current number so the code is

play10:56

even less verbose than before and as you

play10:59

can see here it basically works as

play11:02

expected so in a few seconds we should

play11:04

see our values

play11:05

start ticking there we go okay so as you

play11:08

can see it's a lot nicer when you're

play11:10

using hooks a lot less verbose you don't

play11:12

have to do a lot of setup and even

play11:14

better you can actually easily extract

play11:16

this use effect hook into a new hook

play11:19

let's call it use absorbs or use

play11:21

observable is what we want right and in

play11:25

this case we're gonna pass in an

play11:26

observable and we're gonna pass in a

play11:29

setter function because well observable

play11:31

has to set something right and I'm just

play11:34

going to extract this here and say okay

play11:37

so we're gonna listen to observable and

play11:39

use a setter function for the result and

play11:44

in this case since it's a custom hook

play11:47

it's gonna depends so we're not gonna

play11:51

run it once but we're actually gonna

play11:52

change it based on the observable and

play11:55

setter that are passed to it so in this

play11:57

case I can just write in my coat okay

play12:00

use observable we're gonna use square

play12:02

numbers and we're gonna use set current

play12:04

number right so once we say that we

play12:07

should actually see the numbers working

play12:08

as expected there we go

play12:10

okay so this is all nice and easy but so

play12:14

far you know it doesn't exactly seem

play12:17

useful right so we've been doing all

play12:19

those things and it's kind of looks nice

play12:22

a synchronous and everything but how do

play12:24

you actually apply that in real world

play12:27

well let's implement a real world

play12:29

example that is probably in showcased

play12:32

for well just about every possible rxjs

play12:36

demo I think Auto suggestion or you know

play12:40

search suggestion basic right so in this

play12:42

case I'm gonna use the Pokemon API so I

play12:46

got the URL over here and which is

play12:50

basically gonna have a function that

play12:52

fetches gets up gets get pokemon pokemon

play12:56

vine eight right so we're gonna have a

play12:58

function that takes in a name and it's

play13:01

gonna be a synchronous so it's gonna be

play13:02

a promise and then what its gonna do

play13:05

it's gonna fetch okay a weight fetch our

play13:10

URL so the one that I have this is the

play13:12

Pokemon API and it basically has 2,000

play13:14

Pokemon so which is going to grab the

play13:15

whole array still seeing

play13:17

unison you know in reality you would

play13:19

have a proper API that does the search

play13:20

for you but in this case we're gonna in

play13:23

this case we're gonna simulate the

play13:25

synchronicity okay and then we're gonna

play13:28

get results as Jason and in this case

play13:34

it's gonna be results and we're gonna

play13:36

name them all pokemons right so we're

play13:38

gonna extract this and then what I'm

play13:41

gonna return is I'm gonna say okay all

play13:42

pokemons filter Pokemon so that Pokemon

play13:48

name includes our name right so

play13:52

basically just filter me everything that

play13:54

the API returns with the input that user

play13:58

provided right so simple enough now we

play14:01

don't really need all of that anymore so

play14:04

I can just actually kill that but that

play14:06

will break our app so I will not do that

play14:07

just yet now here's the thing let's

play14:11

implement the search first right so

play14:12

let's I guess we can just kill that

play14:15

because we're not using we're not gonna

play14:16

be using it anymore right so we're gonna

play14:18

have search and we're gonna have set

play14:22

search right so this is gonna be our

play14:23

inputs and in this case I'm just gonna

play14:26

create input type text placeholder

play14:32

search and then we're gonna have value

play14:35

search and on change is gonna be handle

play14:39

search change because we need to do a

play14:42

bit more than just set the function so

play14:45

sorry set the value right so get our set

play14:47

handle a handle search change it's gonna

play14:49

take an event and we're gonna get the

play14:53

new value

play14:54

this is gonna be events targets value

play14:58

and in this case first let's just set

play15:00

search new value right so this will

play15:04

basically allow us to type in stuff in

play15:06

the search box and now we have it saved

play15:08

to the state so it correctly updates now

play15:11

while this works and everything it's not

play15:14

exactly does it doesn't search for

play15:16

anything right so how do you actually do

play15:18

that well we need to create an

play15:21

observable and then we need to apply

play15:24

operators to it right so of course we

play15:27

could create a new observable on every

play15:30

or change but that doesn't sound

play15:31

effective luckily for us the rxjs has a

play15:34

very handy thing called subjects and in

play15:38

this case we can take a behavior subject

play15:40

that is gonna be our preset pipeline for

play15:43

the value processing right so let's

play15:45

create a new search subject right so I'm

play15:50

gonna say okay we got our new search

play15:51

object which is gonna have the default

play15:53

value that is equal to nothing right so

play15:56

this is our user I can actually remove

play15:59

all of that because we don't really need

play16:01

that we do need to use observable okay

play16:04

so we created our subject now what we

play16:07

need to do is say okay search subjects

play16:09

next right and say new value okay so we

play16:14

pass our new value to the search subject

play16:16

which means whoever is subscribed to it

play16:18

will actually get that value so I'm

play16:21

gonna create a new state which is gonna

play16:23

be results and set results and it's

play16:27

gonna be use stage it's gonna be an

play16:28

empty array because we're expect an

play16:30

array of pokemons right and in this case

play16:33

let me just do a div here and I'm just

play16:38

gonna json stringify results now to

play16:42

write so that we can actually see how it

play16:44

looks so in this case it obviously looks

play16:45

as an empty array now what we want to do

play16:48

is we want to use observable right and

play16:51

we're gonna use our search subject and

play16:55

then we're gonna use set results to

play16:57

update it right seems straightforward so

play16:59

once I start typing it should set the

play17:02

values which you know it works but

play17:04

that's not exactly useful right because

play17:06

we don't really do anything to that

play17:07

subject now let's create search results

play17:11

observable which will actually do all

play17:14

the work for us right so we're gonna

play17:15

take the search subject and type it into

play17:18

a new set of operators that will

play17:21

essentially search for the results for

play17:23

us right so first of all we want to

play17:25

filter the input so that value length is

play17:29

more than one symbol because we don't

play17:31

want to search for something very short

play17:32

and Hamor our server with a useless

play17:34

query is essential right next thing we

play17:37

want to do is we want to debounce

play17:39

time and we want to say okay we want to

play17:41

only execute this after users

play17:44

typing for 750 milliseconds this can be

play17:47

tweaked a bit but you know essentially

play17:49

if user changed his might need way of

play17:51

typing we want to wait for him to

play17:53

actually finish it next thing we want to

play17:55

do is we want to say that okay we only

play17:59

want to change if the value is actually

play18:01

changed so if user type something then

play18:03

changed his mind erased it but then

play18:05

change the value back again we don't

play18:07

want to do the same search right and

play18:08

once this is done we want to merge map

play18:11

in this case sorry merge map come on I

play18:14

want to map our value to a new

play18:16

observable that is going to be created

play18:18

from our get Pokemon by name promise

play18:21

right that is going to be searched by

play18:23

our value so now we need to obviously

play18:27

use this search result observable here

play18:30

and this theoretically should give us

play18:32

the result so if I go into the search

play18:34

box and search for pull bazaar there we

play18:36

go so we get our JSON now let's actually

play18:39

render it correctly because this is not

play18:41

very user friendly right so we're gonna

play18:43

take results I'm gonna map them into a

play18:46

Pokemon which is gonna be a div that has

play18:50

a key Pokemon name and then I'm just

play18:56

gonna render Pokemon name over here and

play18:59

it's gonna be it so very simple

play19:02

rendering in this case you know this is

play19:03

not focus of this video so I'm gonna be

play19:05

very straightforward with it so if we

play19:07

search we got the names of the pokemons

play19:09

and if you know if I start typing like

play19:12

crazy erasing it you can see that it

play19:14

actually only searches for the last bit

play19:17

so it never hammers the server it never

play19:18

does unnecessary work and this is

play19:21

basically what's really great about our

play19:23

exchange

play19:25

white this is basically it's I think we

play19:30

covered all the basic things here

play19:33

obviously they're XJS is pretty old

play19:35

library it's very popular there's a huge

play19:37

community around it so if you don't want

play19:39

to write your own hooks or if you're

play19:40

looking for something very specific

play19:41

there is plenty of existing libraries

play19:44

out there for react for example rxjs

play19:46

hooks that simplify working with rxjs

play19:49

quite a bit so if you you know if you're

play19:51

interested if you don't want to write

play19:53

your own hooks do check it out it might

play19:54

simplify your life

play19:56

that is it from my side for today thank

play19:59

you very much for watching and I see you

play20:00

next time bye