JavaScript Marathon | Beginner to Advanced NgRx with Mike Ryan, Creator of NgRx

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi everyone and welcome to javascript marathon today we have mike ryan who's going to be doing an advanced ngrx training which means that you're gonna everything that mike says is going to be like brand new and you've never heard of it before unless like you are mike ryan right mike i don't think so but maybe maybe there'll be some surprises in there kidding but mike we're so excited to have you um do you want to introduce yourself real quick before we get into the training yeah sure my name is mike ryan i'm a principal architect at a consultancy called live love app and i do a lot of ngrx trainings i'm also one of the uh co-creators of ngrx so i helped create this set of libraries i'll be teaching you or training you on today and i'm also a google developer expert in angular web technologies we're so so excited to have you here cool excited all right well then let's just jump in to it yeah but you know i do have to let you share your screen so i just went ahead and enabled that perfect all right so briefly i just want to quickly introduce ingerex to those of you who may not have heard about it before it was originally founded by rob wormold who is on the angular team at the time and it is a collection of open source libraries for angular and the idea is that all these libraries are built with reactivity so we're trying to supercharge your angular apps with reactive libraries to help you do everything from managing state to talking to your services and we're mostly known for state management and side effect handling and the libraries we're teaching today are really going to zoom in on state management side effects i also want to highlight that it's completely community driven it's all open source and done through volunteer work so if you're interested in ngrx you want to help out feel free to join us at github.com ngrx platform and get involved so the goal for today is to understand the architectural implications of interex and how to build angular apps with it and i'm going to try and show some advanced and direct stuff along the way but first i want to demystify in-jerks just a little bit for those of you who are still having a hard time wrapping your head around it and if you've not gotten your head wrapped around in gerex i think the best way for me to think about indirects is that njx works a lot like angular components so i want to use components to kind of create this comparison between how ngrx works with something that you might already be using today in your angular apps so let's take a look at a quick sample application let's imagine we're building a simple app that lets you search for movies by name of director when you search the app reaches out to an api and loads in a bunch of results asynchronously then you can start your favorite films in the search results so far to build this as an angular app what components would you need to build to create this thing well for the search to work we're going to first need to list out these movies so for every movie we get back from our api we're going to create a movies list item component that renders that individual movie and lets the user hit that favor button from there we're going to need a component that actually shows every single movie in this list based on the results we get back from that api probably using something simple like an ng4 we're going to have a search box that actually lets the user type in the name of a director or some other search term and then finally we're going to have this overall page component that's going to tie all this together it's going to handle the interactivity from that search input load up those results pass into the movies list component which will then show each of those movies list item so the hierarchy of this simple angular app ends up looking a little bit like this you have this search movies page which has all this data and it's going to pass it down using inputs to movies list component that movies list components can use ng4 on that list to show a movie's list item for every single movie in that collection when the user hits favorite on one of those movies list items it's going to emit an output letting the parent component know that hey someone hit the favorite button and that movie's list component will then redirect that back all the way up to the top to the search movies page which will reach out to the api and actually mark that movie as favorited similarly when the user searches for a movie in that search movie's box it will emit an event up to that search movies page component letting it know that he needs to go re-fetch those results so let's take a look at some code to see how this is working at the top of this component hierarchy and i want to introduce a few terms that we're going to use throughout this training so the first i want to introduce is this term called state so as the user searches for movies this list of movies is updating dynamically and being passed through the components and state refers to the data that changes dynamically as the application runs so here the state of this component is this list of movies it's updating dynamically as the user searches for new movies the next term i want to introduce is something called side effects and side effects are essentially when your application needs to communicate with the outside world needs to do something or talk to some service or some api and here our component has to handle a side effect when the user searches for movies we need to reach out to our movies api and load in the new set of movies based on those results we're going to use the phrase side effect to refer to the action of calling out to our services and when this side effect completes we get back the new updated set of movies which means we need to update the state of this component we need to replace the old list of movies with the new list of movies gotten back from our api in this process of changing state we're going to refer to as a state change and so this search movie's page component even though this is a simple angular app it's actually doing a lot of different things it's connecting state to components it has that dynamic list of movies that is passing down to its children when the user interacts with these components it's triggering side effects it's reaching out to our services and making api calls and when those api calls complete it's going to change the state so that all the components are updated with the newest set of information and from a communication perspective we can see that what is happening is our search music page is communicating and getting data from the outside world and passing all of that data down through all these components and when the user interacts these components these changes are bubbling up causing the search movies page component to communicate back to the outside world and that's one of the first things i want to reinforce as part of the ngrx mental model when you're building ngrx apps state flows down your angular applications and as the user interacts with your components changes are going to flow back up just like how those components doing but in an ngrx app we don't want to have components that are doing a lot of different things this first movie's page component as i mentioned is connecting state components handling side effects and updating state when those change in an indirect application however we want to adhere to something called the single responsibility principle and the single responsibility principle states that for any given responsibility you should have a single well-defined module of code that handles that responsibility so this search movie's page component if we're to ngrexify it can't do all these different things instead in an ngrx app we're going to write our components in such a way that they only do one thing and do one thing well and this search movies page components single responsibility in an ngrx app is going to be to connect data to components we're going to change it so it is not handling state changes or calling our api services so the way it's going to connect data components is using something that's a lot like inputs and outputs only they're going to be in jrx versions of inputs and outputs so real quickly let's look at that movies list item component this is one of the children components in our application hierarchy this booze list item component receives a movie through an input and when the user hits favorite it emits an output using the favorite output so kind of just a question for you does this component know who is binding to its input like does this movie's list item component actually know who's giving it a movie no not really this component's authored in a way where it doesn't care where the movie came from similarly does this component know who is listening to this favorite output and again the answer is no it doesn't really care it's just saying hey someone's hit favorite and it doesn't have to know which parent component is actually listening for this output and there's a term for this in engineering called in direction in angular app inputs and outputs offer in direction between consumer and parent so when you're writing angular components using inputs and outputs you're writing them such a way where you don't have to really think or care about who's passing in data into those inputs or who's listening to those outputs and in an ngrx app we're going to do something very similar we're going to introduce in direction throughout our angular applications we're going to try and create interaction between the consumers of state how state changes and how side effects are triggered so the architectural diagram for this i like to call this the million dollar slide because this is all of indrex on a single slide even though it's still pretty opaque at this point but the way androx app is going to work is we're going to split up our angular app into three primary modules of code container components effects and reducers and in order to achieve in direction we're going to have some way for these modules of code to be able to communicate with each other without them knowing about each other and the first way that we're going to handle this communication is through something called actions now actions are the bedrock of an indirect application they are truly the gluten of the ngrx loaf and they are a description of events that occur in your angular applications at their simplest form they just add hair to the simple action interface an action in an indirect application just needs to have a type and that type is a string describing where the event took place and what event happened for example when we get our movies back from our api we could dispatch an action to represent that hey something interesting has occurred in our application our movies are loaded successfully and here are all the movies we got back from our api i like to think of actions as being a global output for your entire application anyone can dispatch an action and any other module of code without knowing who dispatched that action can listen for those actions and do something interesting in response those actions being dispatched the first consumer of actions that i want to highlight are effects now if you've used indirect before your experience writing indirect effects was probably something a little bit like this they are really complicated because this is where all of the hardcore rxjs comes into play when you're building ngrx applications let's take a quick look at what one of these effects looks like and if it's confusing right now don't worry i'll get into more detail here in just a little bit but actions have the ability to listen reactively for actions that get dispatched in your application and when an action is dispatched it can use rxjs operators to compose a service call if we can reach out to our api services make a call and when those results come back from our api we can then map those into new actions which get dispatched back to our store and as all these actions are being dispatched from our components on our effects we're going to have another module of code called reducers that are going to consume these actions and reducers are going to be functions that are responsible for changing state or telling ngrx how to change state they can listen for every single action that gets dispatched to the store and they start off with some initial piece of state now indrex is going to keep this in memory this initial piece of state and it's going to say okay every time i get an action in i'm going to call this reducer with this piece of state in this action and this reducer is going to tell me what the next state should be and i as ngrx mr ngrx is going to keep that state back in memory that new result and every time a new action gets dispatched i'm going to loop in the last state that i remembered along with a new action to figure out what the next state should be so reducers are just responsible for figuring out or calculating where the application needs to go next so from a code perspective they're going to look a little bit like this we're going to use create reducer and we're going to start off with some initial piece of state so in our application when the app first boots up before the users had a chance to search for any movies we're gonna start with an empty list of movies and then once the users search for some movies and we've reached out to our api and gotten some results back we can listen for that movies loaded successfully action we can say okay the next state in our app is the list is no longer empty we're leaving that initial state behind and it says the new state is going to be the new list of movies we just got back from our api so actually the primary way that we communicate changes to effects and reducers but we still need an indirect way for components to be able to read state back out and to do that we're going to use something called selectors and their responsibilities to help you bind state to your components so they're going to serve as the communication layer between the store and the components if it helps for you to think of the store as a database then selectors are kind of like the queries into that database they can be really simple they just take in all the application state and they drill down and get some value out of that state so here this lecture is receiving the global state and it's returning the list of movies out from that store selectors are a lot like a global input for your whole app components can use selectors to read values out of the store without having to know anything about where those values are actually coming from and that leads us to containers we're looking back around to the starting point in our diagram a container is just a special angular component that knows how to talk to ngrx store there's nothing special about it other than the fact that it uses the store service to read data out so here it's injecting the store service from ngrx store and then it's going to use selectors to pull data out and when it pulls data out of the store using selectors it actually gets them back in the form of an observable which allows these components to use the async pipe to respond dynamically as that piece of state changes and when the user interacts with the components we're going to use that store service to dispatch actions back out to let those reducers and effects know that something needs to happen so as we build this interx app during this training i want you to really think to yourself that when we're using selectors it's a lot like an ngrx version of inputs we're just reading in data we don't know where it's coming from but allows our component to say hey i need this piece of data and for someone else to provide it and when we're writing our component to dispatch actions it's a lot like using an angular output we're just saying hey something interesting has happened this component and we don't have to care about who's listening for it or what they're going to do in response to it then that's the next part of the injex mental model we want select and dispatch in an indirect app to be special versions of input and output so now the responsibilities are going to be clearly separated we're going to write container components to connect data to the rest of our component tree we're going to be writing effects today that are going to handle the side effects in our application and we're going to author reducers to handle the state transitions in response to actions being dispatched so as we're building centrix app up we're going to adhere that single responsibility module and we're going to delegate individual responsibilities to specific purpose-driven modules of code so as people this indirect zap this is the mental model state's going to flow down and changes are going to flow up we're going to introduce indirection between state and the consumer of states we're going to use select and dispatch apis and ngrx a lot like we how we'd use input and output in our angular components and all along the way we're going to adhere to the single responsibility principle now i'm going through some demos today if you want to catch up with those demos and see what those are like feel free to go to github.com this dot slash ngrx training all the code samples are there along with the slides that i'm going through so let's take a quick look at a demo so this is the application that we're gonna be refactoring i'm gonna change this traditional angular app to be one that uses ngrx and the app is pretty straightforward it's just a catalog of some books along with the gross earnings that that book made when it went for sale at the top we're adding up all the gross earnings of all the books in the catalog i can click on a book to make some changes to it so i can change the earnings here of this book and hit save i can also create new books with some new earnings of their own and hit save to add those to the catalog this is all being persisted to a database so if i refresh the page my changes have been saved and of course i can also delete books from this catalog and as we do this all this data is updating live and dynamically a code perspective our books page component looks a little bit like this so as i mentioned state is all the data that changes dynamically as its application runs and there's three pieces of state that are changing dynamically as i interact with it the list of books in the catalog which i'm getting back from the api and updating by creating books updating books or deleting books there's also the current book this is referring to the book that is actively selected so i'm on this component and i click on a book it lets me edit that specific book and that's what that piece of data is referring to whatever book is actively being edited and then there's also the total the gross earnings total of all these books added together which is this up just this number up here it's adding up all of these values together to produce the gross total of all the books together this component is maintaining some local state and it's got some side effects that it is using to update or initialize this state for example in ng on init we're calling get books which is loading all the books back from our api and updating those totals similarly when the user selects a book we're updating that piece of state to mark which book is currently selected so this component is not adhering this thing responsibility principle it's maintaining state it's calling side effects and it's changing state when those side effects complete and what we want to do is we want to update this component to use ngrx store so that it is only adhering to the single responsibility principle which is connecting data to components so to start our refactor off we're going to go with we're going to start off by writing actions right we need some way for our modules of code to communicate with each other using this indirection idea and actions are going to be a unified interface that describe events as they occur and these events can come from anywhere these are just data there's no functionality to actions these are just data-driven descriptions of events that have occurred we're not going to put any methods or anything like that on these actions as i mentioned before at a minimum these need to have a type property for example whenever the user selects a book we could define an action that describes the user defining the book and the way we're going to do that is we're going to first have to have this type as i mentioned every action must have a type and it must be a string and this string we're going to use a specific format for it inside of the square brackets we're going to describe where this event took place so this event took place on the books page and then after the square brackets we're going to describe what happened in that location so on the books page the user selected a book now it doesn't just have to have a type property we can add any additional data we want to to actions to describe or give context to it so here we're going to also add the book that the user selected what we can do is we can use the store service now to dispatch that action so we can call this.dispatch and pass in that action to let anyone else know that hey the user has done this thing you might want to handle it along the way we want to add here to something called good action hygiene there's a youtube conference talk on this topic if you just type in good action hygiene you'll find it and it's also pretty well documented in the in jrx documentation but i want to go over a few of these rules about what good action hygiene means as we're writing actions we want to make sure that every unique event in our app gets a unique action so every time the user can do something unique we're going to write a unique action for it we're also going to group actions by source so in this example we have all these actions representing that the user is doing some things on the books page we want to group in our code all of the actions that the user can take on the books page into one module of code and this one's perhaps the most controversial but actions are never reused when you write an action it only gets dispatched in one place in your angular application it's not meant to be reused you're never going to dispatch the same action in more than one spot in an angular app that kind of ties into that first property every unique event gets unique action therefore every unique call site to dispatch probably gets unique action too so if we're gonna add hair to good action hygiene we're gonna want to create a group of actions that represent all the things the user can do on the books page they can select a book they can add a book they can update books delete books they can even enter the page to create actions we're going to use a function from in generic store called create action and this is going to give us back a little factory for creating instances of actions so here i'm creating an action that represents the user entering the books page i call create action and the first and only required parameter to supply to it is that action type so here i'm saying that the action type is books page in the square brackets to represent where it's coming from and enter as the event that we're capturing like i said what we get back from here is a little factory so now this enter variable is a function that we can call to get back an instance of this action so our component now we can call that enter factory to get back an instance of that enter action and pass the result back to sort of dispatch create action takes a second parameter which allows you to describe the properties or any additional data that you want to give actions so for example when the user creates a book we probably want to capture all of the parameters that the user typed into that form that they had to do to create a book and add that as context to this action once we've done that using this props function what we can do now is when we call the create book action factory we supply those parameters in as an argument to the great books factory allowing us to add that data to those actions so what we're going to do is we're going to have a file that describes all of the actions that can be dispatched along with the data that we want to attach those actions we're going to group them all up once we've done that we're going to go into our books page component we're going to inject the store service and we're going to update it so that we're dispatching each of those actions as necessary in each of these handlers an advanced tip for teams that are using ngrx is to do something called event storming this isn't something i've invented there's a wikipedia page on event storm inc but it is a team building exercise that allows you to identify all the events that are arising in an application and it's great activity because the output of it is a really great input to actually authoring your actions so to do events forming the way i recommend you do it is to grab some sticky notes and then as a group looking at the design of this feature you're about to build identify all the events that are occurring in that design what are all the ways the user can interact with the design that you're looking at then you can identify all the commands that are causing that event to arise so maybe the user can select a book and the command is the user actually clicking on the uh book card itself once you've identified the commands identify the actor who's actually causing the event to occur so in a lot of these examples today it's the user that's going to be causing the event to occur they're going to be the actor in the system so now you've got some really interesting piece of information you've got where it's supposed to be this where are the events coming from the books page what caused the event to arise well the user clicked on a book who is the actor well it's the user and then finally you can identify all the data that needs to be attached to that event to give that event meaning or context and the output of this exercise is basically your ngrx actions i recommend keeping actions close to where they need to be dispatched in your code base and we'll see this in the demo today but you want to keep the actions and the definitions of those actions close to the components or the effects that are actually dispatching them so that let's write some of the actions that we're going to need for our books page component so in the code base i'm going to go to the books page library and i've got a little actions library right next to it where i'm going to put my books page actions and inside of here what i want to do is i want to do this event storming exercise to identify all of the events that can occur on this page so if you look at this page where all the events that can occur well i can come to this page i can enter it right i can click on the link in the menu to come to the books page i can also select a book i can delete a book i can save a book or i can create a book so what are our the events that can occur here in this system we can enter we can select a book we can actually clear the selected book by hitting cancel we can create a book update a book and delete a book now who's actually doing this who's the actor well in each of these cases the actor is the user and to denote that what i'm going to do is i'm just going to say it's really the user on the books page i'm going to simplify this a little bit i'm going to say that user is kind of inferred as part of this so i'm just going to say the books page is where this is happening yes the user on the books page is doing this once i've done that i have my action types and i can actually use these to go ahead and create the initial definitions of my actions i'm just gonna do some editor tricks here real quick to get these initialized now for each of these i'm going to have to give them specific names and then i also want to give them individual i want to give them any data or context that is needed to give this action some definition so for example this next one select book well i probably want to know what book the user actually selected here so i'm going to do is in my second parameter to create action i'm going to use the props function here to specify any additional pieces of data that give this action context so the user selects a book i probably want to know what the id is of the book they selected and i can keep doing this exercise and what i will end up with are going to be the list of actions that represent each of the user events on the books page so we have the user selecting a book we have the user clearing the selected book the user creating a book and here we have what book that they just created these are updating a book and here we have the id the book that they've changed along with any changes they've made to it and we have the user deleting a book with the id of the book that has been deleted now that i have my actions authored what i want to do is i want to go into the books page component and update it to dispatch these actions as they are or where the um these events are actually being handled by that component do that i'm going to the books page component and the first thing i need to do is actually to inject the store service so you can import store from at njrx store once i have it i'm going to put it here i'm going to say private store is of type now i have a few actions to dispatch i have to dispatch each of these actions and each of the event handlers that is handling event calls in this action we dispatched so for enter for example i can call this.store.dispatch bookspageactions.enter so here i'm using the ng on emit lifecycle hook to capture the event of the user actually entering this page and the user enters this page i'm going to call this.store.dispatch and i need to supply it an action and to create an instance of the action for this enter event i can call bookspage.actions.enter and i can call this to get an instance back out of it similarly for saving a book what i can do is i can go into the event handler for saving a book and i can call this.store.dispatch and here i need to give it an instance of the create book action that we authored earlier something called bookspageactions.createbook and this time i need to actually give it some data i need to give it the book that the users created which i can pass book props into that i'm getting in my event handler for updating a book fairly similar we go in here and i can call this sort of dispatch books page actions.updatebook supplying the id of the book that's being updated along with the changes the user has made to it for deleting a book again very similar i can call this the sort of dispatch i can pass in books page actions.deletebook with the id of the book that's been deleted finally for clearing the selection i can go to remove selected book and i can call this.store.dispatch clear selected book and for selecting a book this that sort of dispatch select book once i've done that i've wired up this component to dispatch actions but nothing is meaningfully happening with these actions yet because we're just dispatching them no one's listening for them but before we get into writing the bit of bits of code that are necessary for listening for them we actually haven't written all of the actions we need to write yet you see we've written all the actions for capturing when the user interacts the page but in an interx app there's more than one actor typically in an application we like to think of everything being a stream and an ngirx application and there's one more producer of actions and that producer is going to be our api so if we think about it when we call our api the api can actually return back a few different events it can succeed which is what we hope happens right when we call our api we hope that api calls succeeds we get back whatever data or cause whatever change we're hoping to make but it can also fail we can have an error handler and apis can also can complete and in nzx app what we want to do is we actually want to write actions that capture the success or failure of our apis so in addition to those user actions we actually want to create actions that model the way our apis respond so let's go ahead and go back into our code base now and what i want to do is i actually want to write some actions that represent our api calls so the api calls right now are if we look at our network tab and hit clear there's a couple of things that are occurring first we get all the books back we also have to handle deleting a book we have to handle creating a book updating a book and so each of these are going to be individual api calls that we want to write actions for so if i go into our code base you can see there's already a file for books api actions and what i want to do here is again do something very similar to that event forming exercise and for each of those four api calls loading the books creating a book updating a book and deleting a book i want to write actions for them now in a normal application i recommend writing actions for both success and failure but to save a little bit of time today i'm just going to do the success actions right now so i have written a couple of actions real quickly for loading the book successfully creating a book successfully updating a book successfully and deleting a book successfully and now what i can do is i can go into my books page component and i can dispatch these new actions as well in each of those places where i was making that service call for example when we get all of our ap all of our books back from our api we can dispatch an action representing that those books were loaded successfully when we get back our updated or our saved newly saved book when we call the create book api we can also capture that here by dispatching an action i'm dispatching a book created action with the book that we just got back from the api same thing for updating a book i can go in here update my event handler here to get back the book that was just updated and i can call books api actions that book updated and dispatch the action i get back from that call finally when a book is deleted i can do the same thing i can call this sort of dispatch and dispatch a book deleted action to represent that book's been deleted now that we've written all of our actions it's time to get into some of the consumers in these actions and the first consumer that we're going to author are reducers now reducers produce new states they instruct ngrx how states should be updated and to do that the store is going to call your reducer functions for you and they're going to give it the most recent state that has in memory along with the next action that was just dispatched and reducers can listen for specific actions they actually get called every single act with every single action gets dispatched but they have the ability to listen for specific actions to instruct injuries how states should change when that action gets dispatched and importantly they're going to do all this using something called pure immutable operations which is a really fancy way of saying they're never going to change data that's supplied to them they're just going to create new sets of data or produce new values in response to receiving the last state in the next action so these are just functions they're not going to look like functions because there's a lot of helpers that help you write reducers but underneath the hood i promise these are just functions and indirects are going to call these functions for you every time an action gets dispatched and they start with some initial piece of state that populates the store and the reducer is going to tell indrex here's how to change state in response to this action here's your next state that you need to go to and what the store is going to do is it's going to take that result and it's going to keep it in memory for us and every time a new action gets dispatched whatever the last piece of state was is going to get looped back into this reducer along with the next action and the reducer is going to instruct ngrx once again where it needs to go next so our app as i mentioned there's a couple of pieces of state on this component so if we go back and look at it there's at least three pieces of data that are changing dynamically as this application runs the list of books the current book and the total value of adding all these books together and what we want to do is we're going to update this to use ngrx to manage the state and for right now we're only going to manage two pieces of state using our reducer we're going to manage the collection the list of books along with whatever the active book is and we're going to save that total value for later because we're going to handle that a little differently so to create this we're going to create an interface describing the shape of the state we have the collection and the active book id and then we need to create an initial state object that implements this interface to tell indirect store where the applications should start when it first starts running so before we've even had a chance to load the books from the api or the users have the ability to select the book when this app first starts up our collection is empty and the user has not gotten selected an active book yet sort of set that active book id to be null from there we can use the create reducer helper from ngrx to define our reducer function so we're going to import create reducer from at n direct slash store and the first parameter that create reducer takes is that initial state object we just defined this is going to tell indirect hey this is where you need to start off when my app first starts running every other parameter after that usually starts with this on function or this on handler and what on does is it lets your reducer listen for specific actions so here we're saying okay i want to listen for the action that represents the user entering the page once you've defined the action you want to listen for you supply it a little callback function this callback function receives whatever the current state is in memory along with the instance of the action you just specified you wanted to listen to so here we're going to get back an instance of that enter action and this callback function is responsible for looking at state and that action and figuring out whatever the next state should be so when the user enters the page we're going to keep whatever was in state before in that collection so if we already had loaded books we're going to keep those books around kind of kind of use this as a cache and when the user comes back on this page for the first time we're going to set that active book id to be null and this is how we're going to write these reducers now i'm going to use some special syntax as we write these um if you notice here we're just for the collection we're just keeping whatever was in state beforehand right we're just saying okay i don't want to change where the collection was i just want to set active book i need to be null and we can simplify this a little bit by using something called the spread operator the spread operator is three dots and we're going to use it on the state object to basically clone all of the things that we're in state beforehand and then after we use the spread operator we can specify all the properties we want to change so here we're keeping everything that was in state before but we're going to set active book id to be null now create reducer can actually accept as many of these on handlers as necessary so you could have as many on handlers as needed here you can see that i have an on handler for the user entering the page and an on handler for the user clearing the selected book and in both of these cases i want to set the active book id to be null but change nothing else that's in state now let's be simplified because on in addition to having as many as you want on handlers can actually listen for as many actions as necessary so here we're doing the exact same thing in response to the user entering the books page and the user clearing the selected book and so we can simplify this a little bit we can say okay on the user entering the book page or the user clearing the selected book let's change state so that active book id is set to null now those are pretty simple example because we're not having to look at actions but in a lot of your reducers on handlers we actually want to inspect action or states to figure out how to figure how to create the next state object so when the user selects a book we need to set active book id to be the id of the book that the user just clicked on and to do that we can read data off of those actions so here we're saying okay when user selects a book let's keep everything that was in state beforehand let's set active book id to be action.book id and this allows us to pull data out of those actions with that let's look at a quick demo of how to write a reducer function and we're going to listen for just a couple of the actions that we are dispatching right now so i'm going to go to my code base and what i want to do is i want to create a reducer function and to do that i'm going to look for or i'm going to do this in a little separate from my books page library and instead i'm going to do it in a separate nx library for my shared state so i'm going to go in here into my book.reducer file and what i want to do is first i want to define an interface that represents the shape of my state that i'm managing with this reducer so i manage two pieces of property or two pieces of data i want to manage the collection of books i also want to manage whatever the active book id is i'm going to set this to be string or null so it's a string when the user has selected a book pointing to the id of the book that's selected and it's null if there's no active book i have my state interface i want to create an initial state object that implements this state interface so this application first starts up we haven't had a chance to get any books back from our api so the collection is going to be empty and the user hasn't had a chance to interact with our page yet so the active book id will be null now that i have my state interface and my initial state object i'm ready to write my reducer so to do that i'm going to create a variable called reducer and i'm going to use the create reducer helper from ngrx store and it requires at least one parameter which is that initial state telling in jrx where to start off when the application first runs after i've given it the initial state object i can then use on handlers to listen for specific actions to change state so i can say okay when the user clears the selected book or when the user enters this page let's change state such that we keep everything that was in state beforehand but we set active book id to be null and when the user selects a book let's change state so that we're keeping everything as in state beforehand but we're setting active book id now to be the id of the book the user has clicked on this is a simple example of a reducer we have an on handler that's listening for clear selected book and the user entering the page and in both of those cases we are telling indrix that the next state should be whatever was in state beforehand using the spread operator but we also want to set the activated null and when the user selects a book we want to keep everything that was in state beforehand but we want to set active book id to be the id of the book they just selected so we've got this created but we haven't quite wired it up yet so what i want to do now is i want to get this wired up by setting up the rest of my store so you can start to see some of the some of these results so set the store first you need to think a little bit about what the store is so the store is kind of like the fanciest global variable in the world all of the state inside of indirect store is managed in a single variable in this state tree now nothing is allowed to change the state any you can read state from it but nothing is allowed to change it we call it the state inside the store immutable nothing can change it instead what happens is the store is going to figure out what next state should be by calling the reducer function that we just wrote and from a hierarchy perspective what this will look like is something like this like i mentioned it's a bit of a tree within grx you organize multiple reducer files into feature modules and we're going to use an api called storemodule.4 feature to register all of our reducers all of our application state then is a collection of all the features that have been registered using storemodule.4 feature another thing that we're going to want to do is we're going to want to use the stored dev tools to instrument our store this is going to give us powerful debugging tools to introspect our ndrx store and to see what state is contained inside of it let's take a look at what this is going to look like from a code perspective so right next to the books reducer file i have this state file and inside of here some things have already been set up for us you'll see here on line 25 i have an ng module that's calling store module.4 feature this is how we're going to register all the reducers that belonged to this feature module every feature module needs to have a feature name or a feature key so because this is the shared state module for the books i'm going to call this the shared books feature after we set up our key we then pass in all of the reducers that we want to register this is going to be a dictionary naming each of the reducers we want to set up so do this first we need to update this shape or the state of this entire feature so we have this books reducer and it has a little bit of state contained within it but this entire feature needs its own state interface as well so to say all the books states is managed underneath the book's property so the book's property in this feature points to this state interface that we created for our books reducer once we've done that typestrip is letting us know that we need to actually register the reducer function for it so we're going to say okay for the book's state manage it using the books reducer these reducers are wired up using store module.4 feature and so now the hierarchy of this is going to be if we kind of go back and look at that hierarchy diagram our application state has one feature and that feature is the shared books feature and we've set up our books reducer to be a child within that feature now we have this wired up i'm also going to go turn on the dev tools which has already been done for you if you look at app.module.ts you can see on line 37 we're using the stored dev tools module to instrument in jrextor which is going to let us use powerful debugging tools on ngrx to install it those dev tools what you want to do is you want to look up the redux devtools extension and it should be available to you on the google chrome um extension store i believe it's also for firefox a number of other browsers as well once you've installed the redux dev tools we can go back to our application and there'll be a new tab in our developer tools called redux and if i select that you can see that it's already connected to our ndrx application and we can see all the actions that we wired up earlier so now as i delete a book we can see an action getting dispatched for deleting that book in addition to seeing all the actions that are being dispatched i can also go into the state tab and i can inspect the state of our application so here's our entire state tree you can see here we have our ngrx feature module shared books and underneath it we have our books reducer and so the book state right now we have that empty collection we have the active book id set to null and if i select a book and we go look at that action you can see that active book id gets set to the id of the book our reducer is being called and calculating what's next in fact i can use this diff tab to quickly see what exactly is changing in each of these action calls when i hit cancel to clear selecting it i can click on that and see that our reducer is being called and setting that active book id to be null so now we've set up the store we need to go back to our reducers and we need to update that reducer to listen for the rest of the actions that we're dispatching and to do that we need to talk a little bit more about this idea of immutability so as i mentioned all the state contained within the store needs to be unchanged we are not ought to mutate it instead a reducer is going responsible for updating it to be whatever is next and to do that i've added a couple of simple helper functions which are what you're welcome to go into and look at a little bit more but these helper functions are going to help us update this state in an immutable way it's going to let us add books to our collection update books in the collection and remove books from the collection without actually changing the array instead all these helper functions are going to create new arrays with those changes applied so these helper functions we can now go into our reducer and we can listen for the rest of the actions that we have not quite set up listeners for yet so let's go into our books producer and let's reduce some more actions so first let's listen for whenever the books are loaded from our api when the books are loaded from our api we want to keep everything that was in state beforehand but we want to set the collection to be the list of books we just got back when a book is created we want to clear whatever the selected book currently was and we want to use that create book helper that's already been supplied for you to add the newly created book into the collection when a book is updated we want to do something similar we want to set the active book id to be null we want to clear the active book id but we also want to update the book in the collection using the newly updated book we just got back from our api and then finally when a book is deleted we want to remove that book from our collection so if i hit save now and go back to our application when it runs when the book's loaded successfully we should see that all those books got added to the collection that we got back from our api when i create a book and hit save we can look at that book created action to see that it did indeed insert that book into the collection if i click on it to edit it and change those earnings and hit save we should see that the earnings property was updated so i think we changed and finally if i delete that book we can use these dev tools to see that the book was indeed removed from the collection so now that we're updating state inside of the store we need to update our components to actually read the data out of the store instead of maintaining its own local state and to do that we're going to need to use selectors now selectors are like the database queries for a database they allow us to query data out of the store and they're really neat because they recompute dynamically this is going to allow us to build reactive angular applications that change dynamically in response to those reducers producing new states they're also super super performant selectors are the key to ngrx applications being really fast because they fully leverage memorization to avoid unnecessary calculations and they're fully composable we can use selectors to create new selectors to create new selectors so let's take a look the simplest selectors are these property selectors all they do is they receive the state object and they get out some property on that state so here we have a selector to select all the books which is fairly straightforward we can just say okay given the state let's use state dot collection to get those books back out if we wanted to get the active book id we could call state dot active book id to get that piece of data out but what if we wanted to select the active book right we have a selector to get all of the books and we have a selector to get the active book id but what if we wanted to get the actual instance of the book that's being selected well selectors are composable we can actually use these two selectors to figure out whatever the active book is so here i have select active book you can see i'm getting the list of books i call my select book selector and i'm getting the active book id by calling my select active book id selector and once i have these two pieces of information i can call books.find on that array to find the book that matches the id of the active book id or i can return null ngrx gives us a utility function to make writing these kinds of selectors a little easier we can use create selector and pass in that select books selector and the select active book id selector to quickly create a select active book selector what we're doing here is create selector takes any number of parameters the first n minus one parameters are all the selectors we want to consume and the final and parameter is going to be a callback function that takes the results of all those previous selectors and gives you those results inside of this little callback function that you can then use to figure out or calculate the piece of state you're trying to create and what's really great about this api is create selector is super super smart create selector will only call this callback function whenever the results of the select books or the select active book id selectors change so these selectors will memorize or keep or retain whatever value you produce and will only call your callback function your little factory function whenever its inputs change giving you a really big performance win now i mentioned earlier that we were not going to maintain the total gross earnings inside of the state and the reason for that is we can actually use a selector to figure out whatever the gross books earnings should be by using create selector we can use or consume that select books selector and say okay whenever any of the whenever the books collection changes let's take those books let's call our calculate gross books earnings function on that list of books to figure out the gross earnings total and if you notice the function signature of our projector function that final parameter matches the function signature of calculate gross books earnings and so we can simplify this a little further and just say okay we're going to create a selector that selects all the books and then passes all those books to calculate gross books earnings now to mention a couple of things about this as you can see selectors are consuming other selectors and this is going to create basically what's called a graph of data so each of these white dots white circles represents one of your selectors and what we're going to do in our ngrx application is some of these components are going to listen to these selectors for example let's say that the orange circle is being actively consumed by one of our components whenever the component subscribes or applies that selector interex is going to intelligently call all of the selectors that it depends on and calculate their results as well to build out this data tree and ngrx is really really smart if a completely different component somewhere else subscribes to another selector that shares some of the dependencies of the first lecture that was being subscribed to indrex is not going to recompute all of the pink circles it's going to retain or save those values and pass them in instead indirects are going to intelligently only call this the selectors that are newly dependent from that so keep this kind of graph in mind that you're building out this rich data graph and i'd encourage you to optimize your angular applications by writing a ton of these selectors with few inputs it's okay to write lots and lots of small selectors and change them together using composition you'll probably get the best performance out of your angular application if you write it this way let's take a look at a demo of how to write a few of these selectors so i'm going to go back into our code base and i'm actually going to write these selectors in the same place where i wrote my reducer and the reason for that is they're really a tied to this state interface the reducer cares a lot about the shape of state and so will selectors so i tend to keep these inside of the same file just because they're so related in terms of the concerns that they're managing and at the bottom i'm going to write a couple of selectors first i'm going to write a select all selector that gets all the books out of state and to do that i can just call state.collection i also want to write one that gets me the active book id so here i'm just creating a function that gets in state and returns back out active book id using these two i want to write one that gets me the active book and to do this i could say okay well all my books can be used can be retrieved using select all on state and the active book id can be done using my select active book id selector on state once i have this i can return books.find and look up the book whose id matches the active book id or return it null i can optimize the performance of select active book by instead using create selector to get some memoization here so i'm going to rewrite this i'm going to call this the one looked active book with bad performance i don't write one with better performance by using my create selector helper from ngrx store now the format of this is fairly similar you can see here that we have inputs to the selector and then we have what's called a calculation to figure out the piece of state we want to calculate and create selector works very similar at the top we list our inputs so we need all the books using the select all selector and we need the active book id using the select active book id selector and then the final parameter is going to be a callback function that receives the data from our inputs so since select all is first the first parameter to this projector function is going to be the list of books and since the second input is the active book id selector the second parameter of this projector will be the active book id so there's a correlation between the order of our inputs and the order of the parameters for this projector function and now that i have this data you see tie trip is inferring all of our types correctly here we have act books as a collection active book editing string or null we can do our calculation inside of this callback and so now we have again these are identical in terms of what they do they both figure out what the active book is but the second version's got better performance because we're using this create selector helper which is going to give us a memoized selector that only calls this projector function when any of our inputs change so i can go ahead and delete the version with bad performance and finally i want to write one to select the earnings total and to do that i can say okay i've got my state i'm gonna need all of my books and then i can return calculate books gross earnings totals on that list of books to figure out the total value of all my books my collection and again this doesn't have the best performance we're not leveraging the memoization that creates lecture gives us so we can refactor this to instead use create selector and here our first input is select all once we do that our input here will be the list of books we got back and now we can call calculate gross earnings on that list of books but if you notice the function signature of our projector function and the function signature of calculate books gross earnings are the same so i can simplify this once more by just passing that in as a projector function don't let this confuse you this is still the input and this is the projector it's just we can pass this function directly in as the projector instead of having to write one out by hand so we've got some selectors but we need to do a little bit more work and that we need to make these available to the rest of our application to actually consume you see these only work on this local piece of state but as i mentioned before ngrx gives you your entire state as a state tree organized by features and so we need to go to our feature state module and we need to export our selectors here to do that first we need to write a selector that actually gets all of the book's state now to get our book state i'm going to use create selector and i've already got its lecture written here called select shared book state this is going to get us this entire feature modules worth of state and once i have that once i have the shared books feature state i can drill down to get the local book state from that object now that i have that i can use the local selectors i wrote here along with this global selector that gets me all a book state and i can compose them together to create versions of these selectors that can be used on the entire state object for example if i want to be able to select all the books out of the entire state graph i can use select book state to get all the book state and then i can say from books select all so what's happening here is i've got to create selector and i'm getting all the book state so i've got my book state here and now i can call this select all selector which works on books state and pass it in to get all the books out from there but because the signature of this projector function matches the signature of this selector i can simplify it by saying something like this and i can do the same thing for selecting the active book i can say okay using the books state use the books selector to give me the active book and to get the earnings totals using the book state give me the earnings totals using that local selector so now that i have these two pieces in play it's time to actually consume these selectors inside of our books page component now right now our books page component has local state on it it's got the list of books and what we're going to do is we're going to replace this with an observable representing that piece of state so instead of having that local array of books on it we're going to have an observable of an array of book models and to populate or initialize this observable we're going to use our selector we just wrote along with the store service to select all of those books and that will return to us an observable of that piece of state that will emit a change every time the selector calculates a new value once we have this set up we can go into our template and instead of just consuming that value directly where you can now replace it with a call to that observable so books and then then to unwrap it or to consume the value inside of this observable we're going to use the async pipe to subscribe to the changes of that observable so let's take a look at how we're going to refactor this component now to use these selectors we just wrote so i'm going to go into the books page component and the first thing i need to do is i need to change this so it's no longer maintaining local state so i'm going to remove all these property initializers here and then what i'm going to do is i'm going to change these to be instead of just their raw direct values i'm going to wrap these up in an observable i'm going to put a dollar sign at the end of each of these property names just to denote to me that these are each streams or observables once i've done that i can see my editors give me a few errors and things i need to go change and the first thing i need to change is i need to actually initialize each of these properties in my constructor so i can say that these dot books well to get an observable of all the books and states i can call store.select select all books to get the current book i can call for select select active book and to get the total and call store.select select books earnings totals so this is populating or initializing each of these observables by connecting them to state contained in our store which we are selecting out with our selectors now this component's not maintaining any local state instead it's reading all of its state from the store so any place in this code that was referencing the local state now i can just delete it because that's all being managed by our reducer now i can remove each of these little functions or call sites for us maintaining that local state and hit save and the final thing i need to change is the template because these are now observables instead of being direct values so i'm going to update those names to add that dollar sign at the end and you can see that i'm being told that the types don't quite match observable of an observable of a number is not assignable to a number so to get to the direct value i'm going to use the async pipe to subscribe to that observable and do this in the rest of these places so i've updated my template and now i should go back to my app and it should still work i should still be able to create a book with some earnings hit save see all the things that are updating live but there's a new trick here so i'm going to make some changes i'm going to change the adventures of pinocchio to have a few more earnings hit save i'm going to delete this book here i'm going to select a book i'm going to hit cancel and if we go look at each of these actions again we can see that the state was changing but now that our component is bound to the store we can actually use the rest of these dev tools to do something called time travel debugging so as i scrub here and go back in time to other actions we can see our angular app updating live and dynamically in response to this so i can inspect what my app looked like at various points in time so when we first started our app when we first entered the page before we made any api calls you see we had no books in our collection and the user hadn't selected a book yet once we loaded our book successfully we updated that list of books when the user selected a book we changed that form when the user updated a book we can see that we clear the selected book and the change was reflected in the ui and so we can use these time traveler debuggers to get really fine-grained control and introspection into our angular app in response to each of these actions being dispatched so we've almost finished up here if we go look at this book's page component we've already made some significant progress in refactoring this to adhere to the single responsibility principle it's no longer managing local state or state transitions we've moved those into reducers it's also dispatching actions almost like global outputs to denote when unique events are occurring but it's still doing one responsibility that we need to refactor and that's that it's calling services directly we want to update this component to no longer be calling these services and to do that we're going to write effects effects are the second consumer of actions in indirect's app that first consumer being the reducers we've already written or reduced just listening for certain actions and changing state now we want to write effects that are listening for actions and calling our apis for us and effects are a little mind mind-bending they run in the background of your angular app they are completely disconnected from your components altogether and instead they kind of run as processes independent of your component tree and they're responsible for connecting your app to the outside world this is where you call services i think the hardest part about effects is so far we've managed to avoid writing too much rxjs effects are where we write a lot of our rxjs code because we declare or define effects entirely using rxjs streams so i've already got a book service that i can use to load books by id get all the books create books update books and i'm not going to change anything about this angular service we're still going to write angular services to capture the logic of connecting to our apis what's going to be different is i'm going to write new angular services called effect services these are still classes and we're still going to use the injectable decorator on them but they're going to be a little bit different and that no one's going to actually inject these services these services are going to use probably at least one service called the actions service and this comes from ngrx effects and it's a special observable of all of the actions that are getting dispatched by your components and by other effects in addition to that action service we can also inject any of the other angular services that we've already written that we want to call inside of our effects from there we're going to add properties to this angular service and each property is going to be an independent effect that nrx is going to subscribe to and run for us on our behalf to create an effect we're going to use the create effect utility function from at njrx effects and create effect takes a callback function that produces or creates the effect itself so in effect is this an rxjs stream or observable and it usually though not always starts off by using the actions service and we can use a special operator that comes with indirect effects called the of type operator to listen for specific actions as they are dispatched so here we're listening for the user entering the books page and when the user enters the books page what we want to do is we want to call our books service to load all the books in so we're going to use a higher order mapping operator called merge map to map this action into a new observable and then flat it all back out and don't worry too much about why i chose merge map yet if we have time we'll get into which operator to use for the right effect but for now we're going to use merge map to map our interaction into a call to our book service to load up all the books now the thing that's fun about ngrxfx is once we've called our service we actually need to map that result the success of getting all of our books back into a new action so here we're going to use the map operator to take all those books we just got back from our book service and map it into a books loaded action and what ngrx effect is going to do is it's going to look at this angular service and it's going to find all the effects properties on it and it's going to subscribe to it so this is going to create this little process in the background of our angular application that's listening for every time the user enters the books page and calls our book service load up all the books mapping it to a books loaded action and then ndrx is going to subscribe to this effect and dispatch this action on for us on our behalf to wire it up we can use the effects module from indrex effects and call effects module.for feature passing in all the angular services that we've written that have effects on them to it so let's write our first effect real quick so i'm going to go into our code base and what i recommend doing is i recommend keeping effects closest to the features that are actually going to be using them so instead of doing it next to a reducer what i'm going to do is i'm actually going to keep it close to the books page component and you can see here i already have a file created called books api effects i'm just going to clear some things here out now i've already scaffolded out a service and this service is injecting two things it's injecting the book service we're going to need to use to call our api and it's also got an actions service which is going to be an observable of all the actions that are being dispatched in our application so to create an effect what i need to do is i need to add a property to this class i'm going to call this the load books effect and a define effect i'm going to call the create effect utility function from ngrx and i'm going to return here a new observable that injerex is going to subscribe to so start off i'm going to use the action service i'm going to say okay what i want to do is i want to set up an effect that listens for every time the user dispatches or whenever the user enters the books page causing this action to be dispatched whenever that happens i'm going to call merge map or i'm using my merge map operator to map this action into a service call so i'm going to say okay whenever the user enters the books page i want to call my book services all method to load in all the books from my api now you can see tie trips give me a bit of a warning because what i've done is i've created an observable of a list of books but what i need is i actually need to return an observable of actions here and so i need to map all the books that i get back into an action so i'm going to use the map operator from rxjs i'm going to call books api actions that book books loaded with the book size got back and so now i have an effect that every time the user enters the books page we're going to reach out to our service and call the all method on it to load all the books back map that into a new action that's going to get dispatched back to the store now my books page module you can see here that i have called my effects module.4 feature passing in this angular service directly into it registering these effects so if i go back to my application we can see in our dev tools that we actually see books loaded success happening twice the reason for that is that on enter our effect is running and loading all the books we've not removed the service call from our component so our service is actually being called two times when the application starts up so i can go to my books page component and i can remove the place where i'm calling this.bookservice.all so i can remove this entire get books function in any place that was calling it beforehand so i've completely removed that service call this component is no longer trying to get all the books instead we've moved that responsibility to this load books effect and now when i run the app the app still works and we can see that we're loading our books successfully because this effect in the background listened for this action the user entering the books page and went and loaded all the books for us so we've changed that but there's a few more service calls that we want to try and refactor and that's going to get in some advanced effects topics so this is what our effect looks like right now and as i mentioned we're using this merge map operator to to call our service and flatten it all back out but there's like a lot of mapping operators so what map operator should you use when you're writing your effects well there's four higher order mapping operators for us to consider there's merge map concat map exhaust map and switch map and these each have different or subtly different behaviors on how they actually flatten out our streams and the way i like to think of them is i like to think about my time going to the post office to ship off boxes now let's say i have a package and i want to get it shipped and i go into the post office and somebody is already in line trying to get their package shipped well if we were using merge map what would happen is both me and the other person would both race up to the postmaster with both our boxes in hand throw them at the postmaster and then walk away and the postmaster is going to get them both shipped for us but there's no guaranteed order to whose box is actually gonna get shipped off first all the boxes do get shipped but there's no guaranteed order here and we kind of just had chaotically just ran up to him well that's not quite how it works um instead kind of the fair way that it would work is if we had used concat map let's say me and this person both show up that person gets in line and they're working again their pack and shipped if i showed up now with my package and they're still in line what i'm going to do with concat map is i'm going to get up in line behind them wait for them to finish and then i'm going to get my turn to get my packet shipped off here there's no chaos everyone's package got shipped and they got shipped in the order that we appeared the other person came to the line first so their package got shipped off first i came second so my package gets shipped off of shipped off after them so can cat map is really fair unfortunately the post office seemingly works more like exhaust map than it does in cat map i show up with my package i see that somebody else is already in line i'm like oh this is going to take forever and i just leave i don't even get my package shipped i was like whatever this is going to take way too long for me this person is already in line and they're taking forever so with exhaust map what has happened here is because somebody else was already in line i'm not getting my package shipped that's just forget about it that's not going to happen instead the first person who showed up first and who was in line will get their package shipped off now the way i wish the post office worked was like switch map where i show up for the post office i see somebody's in line trying to get their packet shipped and i just push them out of the way throw away their box and instead i get my box shipped off i completely discard them forget about them forget about their package but that'd be a little rude and so with each of these there's some pretty different behaviors and specifically with three of them there are race conditions with merge map everyone's packages got shipped but there's no guarantee whose package got shipped when with exhaust map i didn't get my packet shipped because i wasn't going to wait around for the first person to finish shipping their package and with switch map the person who was there first their package didn't get shipped because i pushed them out of the way and i threw their box in the trash so there's also some race conditions to consider with merge map exhaust map and switch map so if you're like okay i'm writing an index effect which of these should i use and you're just not sure always reach for concat map it is the safest operator for you to use in your applications but be aware that there's a risk of back pressure and what back pressure means is that since everyone needs to get their package shipped if that line gets really really long at the post office the person who's last in line is going to be waiting a really long time and so with concat map we're processing things in order one at a time and that can lead to situations where your effects maybe start to slow down things are taking a little bit longer to process because you've built up this cue of things that need to be resolved so always reach for concat map if you're not sure but just be aware there's a little bit of a performance consideration there for some kinds of effects so i have this key that i like to use for when i'm writing my effects that tell me which operator to use based on the kind of operation that i'm trying to accomplish so if i'm writing an effect that deletes items it's okay to use merge map right we need to delete all the items but it doesn't matter which order they get deleted in let's just get them all cleared out as fastly and as efficiently as possible on the other hand if i'm writing an effect for updating or creating items it probably matters that i do these things in order and so let's use concat map and again we're using concat map because we don't want to discard any updates or creations like we want all the users updates and creates to be applied let's do them in order so that they happen in the way the user expects them to for non-parameterized queries like the one in our application where we're getting all of the books let's use exhaust map it doesn't matter who got there first because we're all looking for the same thing we all want all the books so whoever got there first and asked for it great we don't need to ask you any new requests let's use exhaust map on the other hand if we had parametrized query like we had a filter on it well we don't care about any outstanding requests to go get those pieces of data let's use switch map to discard any of our outstanding queries and just care about the last one that got issued so using this key if we go back and look at our book's effects merge map's not quite the right operator we're making a non-parameterized query so we can improve the performance and the predictability of this effect by using exhaust map and saying that whenever someone enters this book page let's just get all the all of the books for them if this request is still outstanding when somebody else asks for a book that's okay we'll eventually resolve this anyways so now let's jump back into this code and let's replace this merge map with a call to exhaust map to use the right operator for this type of effect and now that we know which operator to use we can write a few more of the effects that we need to write for example we can write an effect for creating a book so you can see here we're listening for whenever the user creates a book we're calling our book service to create that book and then we're mapping that into a book created action and because this is a creation effect we're going to use concat map to make sure that we are applying each of these creation events in the order that the user dispatched or caused them to occur in similarly we can do the same thing for updating books we're using cat map to make sure that updates are applied in order i'm going to call the update method on our book service and then for deleting books we can actually use merge map to make sure that we are applying all the deletes but we don't care about order we just want to make sure they all happen and so we can do this very efficiently just using merge map and now that we've written effects for each of these api calls we can now go to our books page component and we can remove the book service all together any remaining calls to it can be cleaned up and deleted because these are all being handled by our effects and so now if we run our application i think should still work we've loaded up all the books i can create a book with some earnings and hit save that's all happening thanks to our effect i can update this book's earnings that gets applied thanks to our update effect and i can delete a book and that gets handled thanks to our delete effect and now we've done it this entire angular component has been refactored to remove all the additional responsibilities that we identified earlier this component is now doing just one thing it's using the store service to read data out of the store and connect that data to its components whenever those components are interacted with this component just simply dispatches actions to let some other module code that has no knowledge about know that something interesting has occurred we've written a reducer that listens for those some of those actions and tells ngrx how to update state and response to those actions we've written selectors that allow us to query data out of the store for our components to consume and we've written effects to handle calling side effects or api calls in response to some actions being dispatched so along the way during this refactor today we've adhered to a few of those mental models of an indrex application we've refactored this app so that all the state is flowing down to our components and as the user interacts with them changes are flowing up we've introduced indirection between the state and the consumer of state no one really knows about each other our reducers are written away where they don't know about the effects that cause actions be dispatched or the components our components are written in a way where they have no idea about the reducers and the effects and our effects are written in such a way where they have no idea about reducers and components there's indirection between all these modules of code we've used store.select and store.dispatch inside of our container component just like we would use input and output that container component is consuming data without having any idea where that data is coming from and it's dispatching actions to represent events that it had no idea um that and has no idea who's actually listening for those if you want to view these slides and more they are available to you um again those are at github.com this dot slash indirect training all the slides i've gone through all the code samples and more are are all available to you as part of this training additionally there's a few more talks that i'd like to recommend in case you want to dive deeper into some of the concepts i've covered today so i've already mentioned my good action hygiene talk that will help you author really high quality actions i also recommend a few testing talks there's reactive testing strategies by brandon roberts and i that help you test indrex applications i also recommend authentication with ngrx by brandon roberts if you want to learn more about how to build authentication using this um i also always like to save this one for the end you might not need ngrx i've taught you how to use it today but you probably don't need it for your angular applications and i've given you a talk on some ideas of when you may or may not need indirect angular application there's also a lot more to indirect that i don't have time to cover there are schematics that help you scaffold out a large amount of code that we've done today so you can use schematics with the angular cli or the nx cli to scaffold out ngrx apps there's router store for connecting angular's router directly to your injects application data for making a little bit easier to consume apis if you want to do that kind of thing and there's ngrx.io which all of the documentation and architecture i've gone through is all documented on inject.io if you want to learn even more so i really appreciate your time today mine again my name is mike ryan you can follow me on twitter github and medium at micron dev and i want to thank you for all y'all's time that was amazing mike i feel like if we had even stopped for questions like there's no way we would have gotten through all that so thank you for all that again mike is amazing he's on twitter he's always super helpful so um if you didn't get your questions answered definitely hit him up on twitter uh he's awesome so yeah thank you mike and thank you everyone for coming
Info
Channel: This Dot Media
Views: 707
Rating: undefined out of 5
Keywords: ngrx, ngrx tutorial, ngrx angular, angular ngrx, angular tutorial
Id: iWX7qCGVt9U
Channel Id: undefined
Length: 89min 16sec (5356 seconds)
Published: Mon Nov 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.