NgRx SignalStore: In-Depth Look at Signal-Based State Management in Angular by Marko Stanimirović

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone I'm very happy to be here and today I'm going to talk about angir signal store uh we will see some story about how it started how the initial idea started and also see signal store in action but first let me briefly introduce myself my name is marus stanimirovic I work as a principal front and engineer at company called Swiss Marketplace group besides that I'm core member of anger team GD angular and organizer of angular bgrd group and yeah if you have Twitter you can find me there by using my handle at Mar Dev so the first question for today what is and direct signal store and by definition we can say that signal store is a reactive State Management solution that Prov provides native or first class support for angular signals and it's built with some key principles in mind we try to provide simple and in intuitive apis to have the straightforward apis that are easy to use and self-descriptive it's also lightweight and performance So based on used apis because it's res shakable the size the participation of signals and signals package in the final bundle can be from 300 bytes to maybe 2 kilobytes it's also the idea is to when we use urx signal store to Define our State Management logic in a declarative way and Signal store is also modular extensible and scalable so with signal store we have the ability to Define independent building blocks that can be reused together or independantly to build extensible and scalable applications and Signal stor is opinionated but flexible in the same time so we try to find some kind of Sweet Spot between opinionated and flexible uh API and as Manfred mentioned it's type safe so we try to provide the API that will help us to uh find bugs in the early phase at compile time instead of runtime and let's now see how we can create a simple signal store so to create the signal store we can use signal store function from M signal sign package it works as a pipe from rxj so it accepts a sequence of signal s features and returns a dynamic service in other words it returns a store as a result as you can see I used uh Pascal case here right to do store instead of camel case because it returns a service not the instance of the store so as I mentioned it it accepts a sequence of features and the first base feature is WID state by using WID State we have the ability to add State slices to our store so the WID State accepts initial State as an input argument the next base feature is with computed by using with computed we can add computed state to our store and you can notice that with computed accepts a factory as an input argument and input argument of this Factory will be previously generated signals for every state slce that we defined in this case we defined the to-do array so under the H signal store will generate a Todo signal that can be used here for example to create computed Tod uh signal completed to-do signal sorry and the last base feature is with methods and it has very similar signature to the with computed but instead of computed signals here we want to Define to return a method as a result so in this case we will add add Todo method uh that will update our state and to update the state you can notice that we are using another function called patch state which is again a standalone function that accepts a store as a first and it also accepts a sequence of partial States or partial State updaters as rest arguments we will see that later in action so when we want to use a store we can provide it as any other service I told you it returns a dynamic Service as a result so we can provide it at the component level if we want to manage the local state and then inject it inside of our component and use it as any other service so we can get the signals that we created or that signals are generated for us or use the methods that we defined but you may thinking okay why functional so most of the espe especially let's say local State Management Solutions are class based so far and to find the answer of this question let's see some limitations of the class-based State Management Solutions so I worked with component s a lot and I faced these limitations that we that we'll now see but they are not only related to the component store but to any class-based State Management solution so the first one is typing it's not possible to Define dynamic class Properties or methods that are strongly typed what I wanted to achieve before is because for comp if you use can you raise your hand if you use component store before Okay cool so with component store we extend the base class and pass the initial state to the parent Constructor and what is possible to do at runtime is is to take the initial State and to automatically generate the selector for each component store State slice but the problem is to provide the type for that we cannot dynamically add Properties or methods to to the base class and have that Base Class strongly typed that's the problem next problem is tree shaking so unused class methods won't be removed from the final bundle so in case of component store we have some utilities updator effect patch States set State Etc and if we don't use updator for example it will end up in the final Banner the next problem is extensibility because multiple inheritance is not supported at least out of the box right uh the next problem the next limitation actually is modularity so or scalability we cannot split selectors updaters and effects into different classes I mean it's possible but it's not provided out of the box so then I ask myself okay should we consider a functional approach and yeah I think that we should we should consider that so we should provide a state management solution that solves the limitation that we just mentioned but we should also provide a state management solution that provides a native or first class support for new reactive model that angular framework provides and let let's now see the signal story in action uh so here we have a quick demo you can find it on my GitHub profile uh you can go to repositories and here it is andir signals example this is the name here we can find the source code that we will see today and the first example is very simple we want to create a counter store here we can see the component let me just start the development server so here we can see the component where we want to display the count which is a base State we also want to display double count derived state or computed State and we want to have two methods increment and decrement to increment or decrement the count value and here in the counter store we need we want to define a signal sore that will do this perform this operations so I will export a new const I will name it counter store and as we saw from the slides we can use signal store function this function is imported from the UR signals package and as we mentioned it act as a pipe so it accepts a sequence of signal sore features the first feature that I want to use here is with state is an input argument I want to provide count zero like this this the next thing is to define a derived or computed state so we can use we computed with computed accept a factory function and here we can notice one interesting thing so it generates we can see that it generates a count signal which is type of signal number based of based on state that we previously defined if I Define for example uh M here and say that this is one here we will have two signals we will have count and M but there is one more thing it will generate deep signal so if we have nested State slices it will generate nested signals for each state slice if I do something like this here Ms T1 so now here we will have M which is you can see the type is a deep signal and let's let's quickly inspect the so result is M so here we can access m dot you can see we can access do s which is again type of deep signal we can get its value by executed it as any other signal so this is now T number but if we want we can also access the T which is now the signal type of number right so that's another feature signal store will automatically create deep signals for us okay we don't need this we in this example we only need the the count State slice so let's now create a double count computed signal the next thing is to create increment and decrement methods so I will use wi methods feature and we will have increment and decrement okay co-pilot is not right here right uh so what we need to do we need to use patch State I will remove this and Define them I will not use crment I will not use a row function fun I will use methods like this I mean there is no difference but I prefer this way for some reason so here we can use patch State function we can see patch state is imported from the Ang signals and now we need to pass the store as a first and sequence or updater or partial State as rest arguments so to get the store we will get it here as an input arguments of wi methods feature Factory so here if we inspect the type you can see that here we have a count uh State slice double count computed signal and this state signal is important if we want to update our state we don't need to access it I mean we cannot it's a private we cannot access directly but we need to pass the store as an input argument of patch State and under the hood patch state will take this state signal and update our state as a next argument what we want to do we want to increase the count so as a part State we can pass count and say store do count like this + one this is the first way um the second way can be to pass so for decrement let's use the updator function so we can do also something like this okay co-pilot is right this this time but instead of count here this is how it works so yeah we now Define the counter store and let's try to use it inside of the counter component so the first thing is to provide it because we want to have a to manage a local state to provide a count uh store at a component level so we can add it to the provider's array of counter component counter store like this then what we can do we can inject it so we can use the inject function store is equal to inject counter sore like this and then for count we want to display store. count and for double count store. double count like this when increment button is clicked we want to call store. increment method and when decrement we want a call store. decrement so as I told you counter store is a service so we can provide it and inject as any other service let's quickly inspect its type so I will quickly create a Constructor here if we say store dot you can see that let's store dot count for example okay we need to use this here we can see that it's a signal number here we also have so store. double count we also have increment and decrement methods that we defined uh the next example that I want to show you is how we can create custom features so here we have the same example we have a signal store we have this WID state with computed with methods features that we use but what if for some reason we want to add this logic to multiple stores to have increment and decrement methods and count as a state slice into multiple stores so for that we can create the custom features that provides this and reuse this feature in any store where this logic is needed I also forgot to mention the another base uh feature that signal provide which is wi hooks so with using wi hooks we can add a specific logic when our story is initialized so here we can see the on in it or when the store is destroyed we can use on Destroy and provide some logic when our store is destroyed but let's now see how we can create a custom feature so I created a file and name it counter. feature. TS so here we can export uh new function and we can call it with counter so this feature should provide a counter feature I will return here the result of function called signal store feature so not signal store but signal store feature and Signal store feature has the same signature as signal store so it accepts a sequence of base or custom features what we can do here we can just for example copy this part these three features that we defined paste it here so now every time when we include WID counter into any store or any custom feature we will get count State slice double count computed State increment and decrement methods so here what we can do now we can call with counter like this and you can see that we don't have compilation error anymore double count is available again its type of signal number in the same way here we can access uh increment or count if we need it because this is these apis are provided by the custom withd counter feature right and there is another thing here that is different from the previous example so by using signal store we also have the ability to define a store as a singl tone to provide our store at a root level if we want to have a global state if you want to manage the local the global state with signal store uh the next example is how we can integrate rxj inside of the signal sore so I told you uh signal sore provides native support for signals it has rxjs as an optional dependencies which is by the way one of the goals that angular teams has with angular to have rxjs as an optional dependency in the future but it provides the integration for rxj because arjs is still great solution when we want to handle a synchronous side effects so here I want to let me quickly show you the example so um we have a users component um it's very simple we have the search box and we have the the list when we want to display the list of users so every time when query is changed what we want to do we want to send the HTTP request to our server to fetch uh users by query and to update the users array that will be displayed in this presentational component we also have the E loading flag to indicate the loading so when with query when update query method is called query will be updated but what we now want to do is to every time when query is updated we want to fetch uh users from the API so we can define a new method we can call it load users by query like this and this will be RX method so I'm now importing RX method function from nirx signals SL rxj interrupt sub package or Plugin so NX signal package provide plugin for rxjs interoperability or integration RX method by the way is uh component store. effect it provides the same API if you use component store. in fact you know that uh this function actually returns a new function that can be called with a static value with signal or with observable so inside of RX method we will see right now we can Define the reactive chain of rxj operators that will be executed every time when RX method is called so here as an input argument we need to specify the type of our input argument uh as a generic here it will be string because query is type of string and then here we can use pipe co-pilot is wrong all the time anyway uh so first thing that we want to do here is to add for example let's add the pound time 300 let's add distinct until changed and then let's set loading to true so I will add to and call Patch State okay it's right this time call is loading to true and then after that I want to call switch map like this and I want to use users service doget by query in order to fetch queries to fetch users sorry from the API but now the question is how to get this user service the thing is uh all base features all the features that signal store provides are executed inside of the injection context so we can inject our user service here I can say user service is equal to inject users service this is the service that I previously defined and it has a get by query a method that returns observable of users so we can now use get by query the query we will get the query here as an input argument so we can pass it here last but not least when we receive the response from the API we want to update our state right so I will use top response operator um top response operator uh is exported from The nerx Operators package and this is another new package uh that provides reusable operator s that can be used across NX packages so tap response was previously exported by the NX component store and it is nothing else than top but under the hood it has catch error and it will return an empty observable if error happens so this provides the ability to so it actually we don't need to resubscribe when error happens by using tap response so here if everything is okay I want to say um yeah let's remove is loading only this I want to add users update the users State slice if error happens yeah let's just log the error but we also have here the finalize call back that will be executed in both cases so if we get success or error response finalize will be executed so in that case we can uh set loading to false like this and now as I told you RX method returns a method or function and now we need to call this method so I will use a new another base feature with hooks because what I want to do when this story is initialized I want when this story is initialized I want to call the load users by query method that I defined here and if we pass the value like this because query is a signal you can see if we pass the value like this it will be executed only once but if I pass a signal so reference to the signal as an input argument of this RX method here then this chain of operators will be executed every time when query signal emits a new value when the value of this signal changes actually which means that every time when update query is called query will be updated query signal will emit the new value and then this load users by query uh method will be executed okay the last example is uh using entities plugin so andir signals package also provides another uh plugin for managing entity State and it's very inspired by andx entity package so you can see here wi entities this is another custom feature but it's provided by the core package wi entities feature as we created with counter and we added count base State uh completed or no uh double count computed State and increment and decrement methods in a similar way with entities feature we add entity map and IDs as a base State and it will also add list of or array of entities as computed state so now what we can do here in our store let's quickly inspect what we have here you can see that we have signal uh that contains array of entities array of IDs and entity map as another signal entities plug-in also provides updator but if we compare these updat ERS to to The androx Entity package they are three shakable so here you can notice when we want to update the state we are using add entity updator that is provided by the entity's sub package we don't have adapter and then dot something why because we want to build three shakable apis so in this case only updator that are used in our source code will end up in the final bundle unlike andx entity package um and yeah let's let's quickly see the component it's it's very simple so we have ADD too presentational component and we have to do list um let's see if our code compiles and open to see how it works so yeah counter it should be fine this is the same example we have rxjs integration also um so yeah we have the search here if I type John for example you can see the loading and then the result is display if we go to the entities page here we can add entities um and yeah this is the the picture from screenshot from mpm registries so maybe 10 days or two weeks since we released the first stable version of androx signals it reached uh the first Milestone 1,000 downloads weekly downloads and I also checked the the mpm this morning I think it's right now around 2,000 weekly downloads it's also important to mention that androx signals packages in inspired by some existing state Management Solutions it's inspired by androx store andrex and component store it's also inspired by rxj if you took a look at the rxj version 8 you can find the RX function and Patch State Works in very similar way so RX function accepts observable as first and sequence of operators as rest arguments in case of patch state for example we pass the store as a first and sequence of updator as rest arguments it's also inspired by elf and I used Vue jazz in the last couple of months a lot so I took some inspiration from Vue ja p as well I also want to say big thanks to these amazing people who helped me with implementation suggestions challenges and everything that was needed to implement this State Management Solutions so my teammates from andrex team team Brandon and Alex also big thanks to Gabriel to my former manager Phil and to friends from angular Architects team Riner manred and Mike if you want to follow us you can find andrex official account on Twitter _ iio we also have the profile on LinkedIn andx uh if you have any question or you want to help others uh you can join our Discord server on the following link Discord gg/ Ander we also have the official doc.io official blog and there is one new thing um here on this link you can find the workshops that nerx team will conduct in January and February there are three days workshops about global store component store and direct signals so you can ping you can pick the the workshop that uh you want uh also this is the link of our GitHub profile so if you want to contribute or if you want to see how androx signals for example is implemented you can go there in the end I also want to mention that in May next year we are organizing angular conference in Belgrade so you are more than welcome to join us you can find more information by visiting our website angular b.org and that's it thank you very much for attention thank you
Info
Channel: Manfred Steyer
Views: 4,533
Rating: undefined out of 5
Keywords:
Id: yaOLbKwVRtc
Channel Id: undefined
Length: 29min 51sec (1791 seconds)
Published: Mon Jan 15 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.