Check out a free preview of the full State Management with Redux & MobX course
The "Redux Observable: Mapping Actions" Lesson is part of the full, State Management with Redux & MobX course featured in this preview video. Here's what you'd learn in this lesson:
Steve hooks the pieces of the application together by taking the result, piping it through a series of functions, and mapping the response into the desired action. The decision process for using Redux Observables and debugging using tap are also discussed.
Transcript from the "Redux Observable: Mapping Actions" Lesson
[00:00:00]
>> Steve Kinney: So go ahead, we need to do more now. All right, so we'll go ahead and we'll, yeah, finally make that AJAX request, right. But we have to say what happens next, and so we'll take that result. And so this is why we need the merge map, right, cuz we're taking the result and we're piping that through a series of functions.
[00:00:20]
Which is basically, cool, you got an action, it's of the type FETCH_CHARACTER, fire off an AJAX request. Well, what you wanna do with that result? If you remember when we did the thunks, we had the .then. As we were like, okay, get the response, make sure it's JSON, right, this is the .getJSON, so we'll be a little bit better off there.
[00:00:38]
So we'll get the response, but then we need to take that response and map it into the action that we want, right, which is this fetchCharactersFulfilled.
>> Steve Kinney: Right, so then we'll map it.
>> Steve Kinney: Cuz we need to put the type and stuff on it. We'll take the response, we'll use our action creator with the response.result.
[00:01:05]
All right, prettier, do your thing, cool. So if it's type FETCH_CHARACTER, fire off the AJAX, take that AJAX request, run it through its own pipe. And then transform it into the action and then get it back up to the top level cuz we're nested. There's a learning curve, [LAUGH] right, there's an absolute learning curve.
[00:01:26]
Hopefully that the power is, is like, it's not terrible to think about RXJS as like Lodash for async. Right, so there's a whole bunch of functions, you want it to bounce, cool. You wanna make it so you're not, if there's already a request in flight, you don't wanna fire off another one, neat.
[00:01:45]
You wanna wait a certain amount of time, awesome, right? You can do all sorts of stuff that, if you did this in Redux Thunk by hand, would be really, really tricky. Right, you'd end up writing half of RXJS yourself, probably, so there definitely is a learning curve. But we found, even when we bring on junior engineers, they are able to pretty much pick it up pretty quickly.
[00:02:08]
Cuz there's not too many things that we end up doing. Predominantly, we're doing something very similar to this, right, which we then wrap up in an abstraction, right? Mostly, we're making AJAX calls, right? We might check to see if we already have some data and do something different, we might try to throttle some of them.
[00:02:31]
But in our decently large app, we're not doing all that much. So while this seems a little bit intimidating, one of the things I like is that actions come in, actions come out. I've taken all of this kind of strange complexity, and I've moved it kinda to the side.
[00:02:48]
And I've kept a purity to my Redux in my React application, right? And the Redux observable part is small, the RXJS part is like, okay, what is going on here, right? It's because RXJS is incredibly powerful and does a lot for you, but that means it has a learning curve, right?
[00:03:06]
Redux Thunk, super simple, man, I just match a function, and that dispatches the action when it's done. But you're on your own, and that's a kind of trade off, I don't have a right answer for you, right? We made the choice that in the application, we're trying to set the foundation for the next three to five years.
[00:03:19]
We made the choice that we decided to have patterns and complexity that we are willing to deal with for stability, right? And to know that we weren't gonna have to massively rework stuff later. That's the decision that we made, but it might be very different for you. All right, let's actually get that hooked up now.
[00:03:40]
>> Steve Kinney: I could've just called that epic.js, but here we are. So we've got this create middleware, which is cool. We're gonna import the rootEpic, or just the epic, but the same way we called it a root reducer before.
>> Steve Kinney: From this fetch-character-epic. So now we need to, a little bit lower, we'll go ahead and we'll say, epicMiddleware = createEpicMiddleware().
[00:04:17]
That will create us a new instance, and then we can pass that to the store. And the same way we did with the thunk, applyMiddleware(epicMiddleware). I don't know why I get so much enjoyment out of doing that.
>> Steve Kinney: And then we do need to do one thing, it's the same.
[00:04:34]
Redux Saga comes with the same advantages, where its actions async stuff just using ES6 generators instead, and it's true for both of these. We do epicMiddleware, sorry, with that root and you have to run it.
>> Steve Kinney: Right, and what this will do is, so we give it the store, we put the middleware in place.
[00:04:57]
I've said this a few times now, it sits along the side listening for the stream of actions, and transforming them as needed. So we just have to be like, hey, start listening, we're gonna have some actions flow through here, right? So we've got these actions in place, actually don't need this,
[00:05:20]
>> Steve Kinney: Up here. So we have the searchTerm in there, and we have the ability here. We've got an individual character, that's a kinda stateless prop, it's got this kinda hardcoded URL in there. We just need to kinda map this together, so we'll go ahead and we'll do that.
[00:05:41]
>> Steve Kinney: And this is gonna to be kind of we have that fetchCharacters, remember, that we created the very beginning. And we're just going to,
>> Steve Kinney: Nope, good try.
>> Steve Kinney: Gotta connect, and we're just gonna say, hey.
>> Steve Kinney: Well just connect it in here like we did before.
>> Steve Kinney: null for the map state to prop.
[00:06:12]
>> Steve Kinney: And,
>> Steve Kinney: This fetchCharacters.
>> Steve Kinney: All right, take a look in that reducer.
>> Steve Kinney: All right, so what is it gonna be, it's if, now it's gonna be action.type. We only care about when it's, we could show a loading indicator if we get the first one through. But right now, we mostly care about the second one.
[00:06:55]
So we could listen for FETCH_CHARACTERS, cuz even though the epic is receiving it and transforming it, we still get it in our regular reducer, so we could do stuff, we want actually fulfilled, hold on.
>> Steve Kinney: Cool, and we'll say return this action.payload.characters.
>> Steve Kinney: All right, so we'll have it in place, this little fly-through, when we get some characters in place, we should see that.
[00:07:26]
The other thing we're gonna need to do is, let's just take a look at the actual characters component. It's receiving all the characters as well, so we're gonna need to get those in place. Cool, this will be pretty easy to do, we'll just say,
>> Steve Kinney: Pull the characters off,
[00:07:53]
>> Steve Kinney: Of state.
>> Steve Kinney: All right, so this should have everything we need, now let's fire it up and see. We did a decent amount of coding there.
>> Steve Kinney: Nothing's not compiling, though.
>> Steve Kinney: All right, so let's take a look and we'll see what we're missing. Give me one second just to look, and then we'll round back and see what the issue is.
[00:08:27]
>> Steve Kinney: So I made a rookie mistake, I was trying to code and talk at the same time, and I'm half good at either of those. I'm very good at talking [LAUGH]. What I did was, I passed the dispatch to the characters, when I should have given it to the input field.
[00:08:45]
So I don't need to pass the dispatch there, I just had to put it in, the thing that's doing the fetching of the characters needed to know how to fetch characters. So just a silly mistake where I put it on the wrong component. But that led me to some interesting kind of tidbits on debugging.
[00:09:05]
Which is, in that stream where like it's a function returning another function, it can be a little tricky to figure out how to console log and to be able to debug. So one trick that I like, it's really simple, and I'm almost borderline glad this came up, is that if you need to do stuff in the actual epic.
[00:09:30]
So I also made the mistake of, I had result singular instead of result plural, which is my MO at this point. One of the things that you can do is, hey, I want to interject but I don't wanna interrupt the flow, right? We know that console log returns undefined, it's all weird.
[00:09:49]
We wanna say, hey, I wanna tap into the stream, do a thing, but I wanna take the same value and keep it going. Right, I don't wanna interrupt the stream of events. And so there's a function called tap, tap exists in Lodash too, and it exists actually in Ruby.
[00:10:06]
tap is basically a method or a function that, you do some stuff, and you return whatever object it was originally, right? So in Ruby, if you call as, I wish that JavaScript had it on the object, right? Which is, you call tap, you have some functionality, but then you get the return value is that original object again.
[00:10:26]
Lodash takes an object and a function, you can do a bunch of stuff with that object, but you'll get back the object again. jQuery kind of does this as a default, you can do whatever you want, you're gonna get another jQuery object. So you can actually use, there's a tap operator, which allows you to do a thing.
[00:10:46]
So we'll just say tap, and then what we'll return the value out. So you can say something like value, console.log(value), and again, console log returns undefined, which normally would break your stream. But it'll actually allow you to do a thing, tap into the stream, if you will. Console log or do whatever other things you need to do, and then return that initial value back, right, so it's a great way to debug.
[00:11:13]
Turns out, we can also do some other interesting things in here, which is, we can keep it there as well. We can also, cancel, right, which is a thing that Redux observable gives us. It's one of the reasons why you might wanna pay the complexity cost for Redux observable, right?
[00:11:32]
RXJS comes with a lot of power tools to do things like, I mean, if you did this in Redux Sonic, you might have to fire a new action that said canceled. That then we need to know to ignore the next fulfilled event, I don't want to do that, right?
[00:11:49]
I would rather, I tend to prefer battle-tested libraries rather than my own bespoke solution that's never been out into the wild. So what we wanna do is, if we wanted to implement cancellation, i do wanna show it to you fully working. We'll see it with the tap in place.
[00:12:09]
>> Steve Kinney: Which is, I can begin,
>> Steve Kinney: And this API has some delay on it on purpose.
>> Steve Kinney: It did fix it, let's see, then I typed a bunch more and did some random things, so that's fine.
>> Steve Kinney: Launch the reducer,
>> Steve Kinney: So it says the FETCH_CHARACTER_FULFILLED, reducer characters returned undefined.
[00:12:39]
Let's just check that out for a second, should I have that action there?
>> Steve Kinney: It's just the payload, it's not payload.characters.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops