Check out a free preview of the full Reactive Angular with NgRx course
The "Final Q&A" Lesson is part of the full, Reactive Angular with NgRx course featured in this preview video. Here's what you'd learn in this lesson:
Lukas fields final questions from students such as how to ensure that the application has the last state when accessing a selector from an effect, whether the store is a sort of cache, clarification on what an Observable is, change detection, testing when using the Facade pattern, and a clarification on the index.ts "barrel roll".
Transcript from the "Final Q&A" Lesson
[00:00:00]
>> Speaker 1: If we need to access a selector from an effect, what is the best way to ensure we have the last state of the store there?
>> Lukas: With the latest.
>> Lukas: And so you can access and so I really need to do this. But in your effect, you can say, take from the stored directly and you can say, give me this, give me, I believe it's with latest.
[00:00:32]
So I would have to look that up but I've had to do that once or twice. And because it's an observable stream, then it's the width latest operator, if I'm not mistaken.
>> Speaker 1: I know that cache could be implemented with interceptors but here, the store is kind of a cache, am I right?
[00:00:53]
>> Lukas: Yes. And so, one, on some level, cache is already being implemented on at a selector level. But for instance, if you're like, go fetch this thing but if it already exist then don't go fetch the thing. So this would be like, I'm going to login and I'm going to get my user profile and I don't need to do it again.
[00:01:22]
Because I have it once and so if that exists, then go ahead and do that. So part of the problem is one of the reasons why a lot of times you have to go and call again and again because your state, my user profiles over here but I need it over here.
[00:01:38]
And so now by saying, I've done this once, it's in the store, I don't need to go get it again. And so typically I find by having a single source of truth, that really cuts down all the need for cache because if it exists, then it would just select it and you would get it.
[00:01:55]
But what you could do is for instance, there is some sequence or maybe it doesn't exist is that you could say, hey, dispatch this event. If there's nothing in the store, go make the call and then do it. If not, just pass it through. So you could definitely implement some caching strategy and some logic at, I would say, probably the effect level is probably where I would do that.
[00:02:27]
>> Lukas: Yes.
>> Speaker 3: While we're talking about effects,
>> Speaker 3: I guess the magic with the effects is that fetch and pessimistic update and all the data persistence stuff returns an observable.
>> Lukas: Yeah.
>> Speaker 3: I don't get what an observable is because it implements a publication subscription module or a pattern.
[00:02:58]
>> Lukas: One, I love observables, and I think to really do it justice, we probably would need to well it is a workshop in Front End Masters. Jafar Usein is amazing, probably one of my heroes, he does a really good job of talking about that. But to simplify, this is the 60-second version of this, is that an observable is really a combination of the iterator pattern and the observer pattern because their interface is pretty close.
[00:03:32]
So now within an application, is that unique data, and so an observable will actually provide that data to you and it will continue to provide data as it becomes available. So unlike a promise which is a single-fire and you're done, it'll only ever resolve once, the observable will continue to produce data until the end of time hypothetically via a NXT.
[00:03:57]
So if you think about an iterator, it's like NXT, NXT, NXT, NXT, NXT, so it provides the data. Now the question is, well, what happens if that data is available at some indeterminate time? So using the observer pattern, you're able to say, hey, I'm going to subscribe to this observable string.
[00:04:16]
And whenever this data is available, go ahead and push it to me. So you have essentially your initial output. So we think of data or programming lot of times as input, output, I'm going to give you something, you're going to give me something back. With observables, you flip that around and data is coming, there is data streaming, where there's some event that's producing data that you consume.
[00:04:42]
So dom events are actually a really good way to start to learn observables cuz they happen all the time. Move your mouse over the browser, and whether you know it or not, there's dom events firing. So I'm capturing the stream, so your initial output and then your final input.
[00:04:58]
And so I want to take this and I wanna put it over here. So we'll take this, we'll move it over here. So now you have this encapsulated stream that one keeps to manage the state within the stream. So now you're getting this microcosm but then you can start to stack these operators within the stream and transform your data as you go along.
[00:05:22]
You even can pose streams together, so now with an effect being this asynchronous thing, is it captures an initial event which is, hey, this action object was fired. You're allowed to do any number of asynchronous things and at the end of it you're basically kicking out a new action object.
[00:05:44]
And the point is is that any time that action becomes available it'll retrigger. And then you have all of the operators in between. And so I don't think that that's caught on on React, as far as I know, maybe it has.
>> Speaker 3: Sounds roughly similar to Reduct Saga, which uses ES6 generators.
[00:06:05]
You can keep asking it for a value, and it will-
>> Lukas: Well so the thing is you never even have to ask for the value, it's going to push it to you cuz you're basically observing it. And that's where I think, so I've done a redox implementation in 1.x, and we had to use normal eventing.
[00:06:26]
To where the differences with this, or at least it was there some kind of event thing baked on the back end. This is because an observable stream you're able to just subscribe to it, and any time data comes then it just pushes it to you.
>> Speaker 3: Pushes it to the component that the effect is.
[00:06:41]
>> Lukas: Or anything that's subscribed to it, it pushes the so you'll never have to pull and it will push but unlike a promise, it will push in a number of values.
>> Speaker 3: And then Angular, like the decision to re-render a component takes that effect into account. So if you'd date it in that [INAUDIBLE] changes then Angular will rerender your code.
[00:07:05]
>> Lukas: Right, but what's interesting is you can defer change detection to an observable. So you can say, for this component tree, I wanna turn off change detection entirely because anything that's changed is in the observable strain. And then what happens once at the end, once it fires, then you fire just a change detection, so this very, very fast.
[00:07:31]
When you're saying, hey Angular, you don't have to do change detection. Which Angular 2 and above, it's gotten a lot faster and it does a very good job. But you know what's faster than doing change detection? Not doing it at all. It's just pushing the change detection.
>> Speaker 3: Sure, so that's another win for your single source of truth is you only have one place to look to determine if you need to re-render?
[00:07:51]
>> Lukas: Yeah.
>> Speaker 3: Cool.
>> Speaker 4: So we don't need to test a component if we follow this pattern, like presentation component?
>> Lukas: That sounded almost like a statement and not a question, but you still need to test the component, but if we look at this, there's not a lot of logic here.
[00:08:23]
I think if we have more time, I would actually, there's ways to abstract this out, but when it's all said and done. So let's pretend this is not here. And let's look at this component in particular.
>> Lukas: Actually, this would actually get put in the facade as well.
[00:08:54]
>> Lukas: But in this component, show me where the logic is, any kind of logic that we have. So no, no, no, no, there is a piece of logic? No, and so the surface area to test this component is very, very small. But typically when you focus on testing something, you want to test that unit and nothing outside of it.
[00:09:28]
So I could test this component in the sense of when I create a project or I called this project. I'm going to spy on the facade and make sure that createProject was fired with this project. And so this is where spies come in handy because for something that is basically delegating out, there is no logic necessary to test.
[00:09:53]
But you can say like, when I call this, it's called this, so it's ensuring that it's delegating properly with the right information. Now from here what I would do is I would pass in a project without an ID and I would say, when I called state project without an ID I want to make sure that createProject was called.
[00:10:18]
So I would spy on createProject, and then with this project. And also I could say, also I wanna make sure that update project was not called. And so at the component level, the surface area for testing at a unit level is very small. Now end-to-end testing, this is a lot easier to do now, but I would recommend having some broad level end-to-end test.
[00:10:48]
So typically I think of a user story, so when you're working with business analysts and they're saying, this should do this and this and this. Well, I think Protractor's okay, but I think Cypress is the heat, it is incredibly easy to set up. And so when you set up everything correct, especially using a page-object pattern.
[00:11:10]
So the selector's getting stuff off the page is, you've already implemented it and you can reuse it. Is that you can create, for instance, Cypress end-to-end test I believe is fast, is you could almost write a unit test for it. And so I think you should have testing but here I probably would not be so motivated to do unit testing on it, maybe some very cursory things.
[00:11:35]
But where I would be interested and testing is when we go down to for instance, the reducer or the selector, so I would test reducers and selectors. Now those are super easy because we know that because it has referential transparency. When I pass in state and an action object, I'm going to get something else, you're going to get something predictable out.
[00:12:07]
So this a lot of, when I call this, this is the return, so you have a clear contract and it's deterministic. The same thing with your selectors is that you can say, when I pass this in, this is what I expect out. And really where it gets tricky, I would say, probably the hardest part when you're testing in GRX is back [INAUDIBLE].
[00:12:33]
Because now you're dealing with essentially, streams, marbles and different things like that. It was a really good question. So the answer is, yes but focus on the things that matter in your business logic, that's well suited, there's a clear units of code, and those are testable. So I think even you were talking about 100% code coverage.
[00:12:58]
I would say, that's a really nice idea but you're going to end up writing a lot of low quality tests. So focus on where there's actually units of logic and then test those. Starting with the most critical things and then move backwards. I would rather have a really good end-to-end test versus 40 marginal unit test but I've got a 97% co-coverage or something like that, so it's always quality over quantity.
[00:13:40]
>> Speaker 4: What is the purpose of the index.ts in state module folder? It contains all kinds of constants. Is it a helper?
>> Lukas: So what you're doing in the barrel roll here, so this top level, not this one, but this one right here. So this is exposing your application state as a whole.
[00:14:14]
So all of your individual reducers are now going to be combined into a single action reducer map. And so your entire shape of your application needs to be combined somewhere. So the most logical place is that if are within your state management folder here, I'll just zoom in on this.
[00:14:43]
Typically, and there's some different opinions on this, I like to just have a state folder and then I put the features in that I'm managing. They seem to be combined somewhere. So I believe the best way to do that is in an index.ts file that sits right above those.
[00:15:04]
And so by combining the reducers in a single place, we can then export that and make that available. Now the selectors provide an interface to your application state as a whole. And so for me, philosophically, when you are defining the shape of your entire application state and the way in which you communicate with it.
[00:15:31]
It makes sense to me to have that in a single top level index.ts. Where it becomes I think really helpful is when you're doing computed selectors where you need to combine selectors that involve related entities. And so now if I were to put this in the customers reducer, so now the customers reducer needs to know about the project's reducer and vice versa.
[00:16:05]
And so now by having this up is that I'm able to compose these together at a top level. And so some of these, I would say, are fairly simplistic, but you can definitely, in a non trivial application, start to create some very sophisticated selectors. And that I think needs to happen in a single place and so this is essentially, index.ts, is the public API for your store, and that's why I do that.
[00:16:36]
>> [APPLAUSE]
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops