Lesson Description
The "Custom Store with SignalStore" Lesson is part of the full, Advanced Angular: Performance & Enterprise State course featured in this preview video. Here's what you'd learn in this lesson:
Alex demonstrates refactoring the cart service into a Signal Store, using state, computed, and methods to manage and update application state immutably.
Transcript from the "Custom Store with SignalStore" Lesson
[00:00:00]
>> Alex Okrushko: So we're now going to be going into one of my favorite subjects for, yeah, no pun intended. Well, we weren't going to be using subjects. Let's just revert this thing. We don't need it. This was purely exercise for testing and understanding. All right, so we're going to introduce state management into our application, so right now our cart service as you can see here, it does the PR requests, right, the load tickets, they add a ticket, does the PR requests.
[00:00:47]
But it's also stateful. So it kind of keeps internal state here for ticket IDs and derived state for the count. That is okay, but now we're really mixing two responsibilities into the service to do the API calls and also to keep track of some of the state. So we're going to do is we're going to refactor this cart service and put the state management where the state management, I think belongs, which is a dedicated service.
[00:01:33]
Before I do that, let me just show you what the signal store is all about. Okay, so in this core I again, I'm just going to do the example, say we can have like a to-do store. All right, so we're going to do a to-do store, and we understand this is so hopefully will help us understand what the signal stores are all about.
[00:02:02]
So say we have some kind of interface that will represent our state, for example, we have like book state. And book state will keep track of some books. Right? Book, all right, and again we can have the interface for the book itself, which will be some ID. It should be like string. I will have some price, for example, for the book, we can say it could be a number, right?
[00:02:50]
Title probably important. So let's just do this, and again, you don't have to type it in, this is purely for exercise for understanding what it is. So our state would keep track of these items. So we want to create a state management for this. We can create a signal store for this, and the way we'll create a signal store is we'll create a bookstore.
[00:03:22]
And to create it, usually we have injectables, when we create injectables, we'll create classes, right? The signal store takes a different approach. I'm going to do the functional approach here. And the reason we're doing the functional approach is because that will allow us to compose what we want to be part of that injectable.
[00:03:48]
And then that injectable can be typed properly and used properly wherever we inject it. So let's do with this bookstore, we're going to do the signal store. By the way, at this point, we removed the NgRx before. Let's add it back. Let me just comment this out for a second. If you did remove it from the package.json, let's add it back.
[00:04:19]
So we're going to have NgRx store and NgRx operators, let me just copy paste this in our package. That's the one we removed, I'm going to install them back now. So in the package.json, going to do that and npm install force. Perfect, so now we have our operators and signals, yeah. And NgRx itself has been around for a very long time.
[00:05:07]
It started as the global state management library that is closely related to Redux with events and things like that. That library still around, there was a second library we called Component Store. And yours truly was the main author of that thing. This is the time where we couldn't inject anything without the constructor.
[00:05:34]
So we created a class and it was meant for a more local state, something like tab state that we have right now, it was basically for that to manage more component state that's not needed in the global scope that we want to destroy and create when we navigate to certain pages. Signal Store is the third iteration. It was created for a few reasons.
[00:06:05]
First, inject function became available, and secondly, Angular was moving to signals. So we created a signal based library, and it's called signal store to kind of resemble that as well. All right, so let's inject signal store. We're using it for the first time, so we have to use it manually first import from NgRx signals.
[00:06:35]
So we have signals, so we have a lot of things and again, to cover all of it takes a few days, but we'll have the signal store function. So signal store function is how we would create this injectable store. And the signal store function takes a few functions inside of it to assemble it almost piece by piece, it's like little Lego blocks composability.
[00:07:10]
So we use only when we need to. And those are all also functions. So the first function we're going to use is called withState. And withState, it's also coming from signals, allow us to declare our initial state that the signal store will be taking care of. For example, in this case it is books. And initially it's just empty, I'm going to say that this signal state is working with book state.
[00:07:46]
Right, and again, we could be done. But that's not the point of this, right, right now, it's just like, hey, this is the state that it's responsible for. Now we can provide some computed values as well, right, so then we have the function that's called withComputed. And this withComputed, anything above it, any state that the store has above it rolls into the next function, so we can have the store here.
[00:08:30]
And we say, hey, we can create a few computed ones, and then return. So which computer do we want to do? We can say we can maybe provide the total books, total books, right? And the total books is this store books, which is a signal. Right, this becomes a signal. And there's books, we read the value and the length, this is becoming a new computer, we just introduced computed.
[00:09:13]
The other one that we might want to have is maybe like free books. Free books and the free books will be our store books. And then we'll filter all the books that have the price equal to 0, right? So now we have this bookstore that keeps track of books and computing. I'm going to show you in a second how can you use this, but that's not all, right?
[00:09:53]
This is, again, this is the state, this is derived state. And we also can introduce how we operate on that state. And the way we operate in that state is with methods. And by the way, just to show you how we can save a few lines of code here as well, store is there, we can destructure this immediately, right, so because we're only using books here, we get the entire instance of the store.
[00:10:22]
But we can destructure this immediately and just extract books out of this, right? So you have your signal books. This is standard, there's nothing unique about the signal store here, this is just generally we can remove the store and store every time, we just get the books that were provided by the state. Okay, now with methods, again, we have this store for us available.
[00:10:56]
Right? And oh, by the way, the other thing we can do as well because we're returning the object immediately. See, by the way, I'm not using computed here. Computed, it doesn't need to, since if whatever I'm returning to is functions, they'll be automatically wrapped with computed, so I don't need to say that these are computed.
[00:11:16]
It knows, and because I'm returning the object immediately, I can even make this remove this right, and I just wrap it with the parentheses, right, this is just condense things a little bit. It's exactly the same thing as we had before, but just in a nicer package now. So with methods, what we're doing and do with methods, we can also add a few things, and again, I'm going to be returning the object immediately.
[00:12:01]
But this object will have methods, and one of the methods would just to add book, for example, and this book will be taking the book of books. And what we're going to do here is one good two arguments, yes, because that's exactly right here we go. There you go. So what we're going to do in the book, when we add the book, we want to change the state, for example.
[00:12:37]
And the way we change the state is with patch. Patch state patch state function takes the state as the first argument, so in this case our store is the first argument, and then we can patch it with the new state. So we would update our books. Books and the way we'd update books immutably, which means we need to create a new array.
[00:13:07]
And we're going to spread the store books that we had before and going to add the new book we've seen this technique already in the intermediate Angular. This is how we create the new array, but more interestingly, this is a partial updater so we can patch anything at the top level so for example if we had some other things like I don't know, pencils.
[00:13:47]
Right, of some kind of string array, for example. String array, yeah, the book state doesn't have it. Sure, I just and string array, for example, right? Here, right, so we still don't need to mention any pencils here, so we're not updating the entire object, we're updating only one property. But from that top property on, you have to specify the whole thing, right, so this is called like the partial update, so we're updating books only.
[00:14:28]
And then when we're updating books we need to pass it in. So let me just remove those pencils for a second. They are not really needed. So now what happens under the hood? Is signal store function will take all of these. We call them signal store features, which is withState withComputed. It's also signal store feature.
[00:15:04]
And all the other ones, and combine them and will create the injectable for us. The cool thing about this is, I'm going to show you a few things, so you can do as a first argument. You can specify config for this exactly, for example, if you do want to do provided in root. That could be specified as the first argument here.
Learn Straight from the Experts Who Shape the Modern Web
- 250+In-depth Courses
- Industry Leading Experts
- 24Learning Paths
- Live Interactive Workshops