Application State in Blazor Apps

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
carl franklin's going to be talking about applications franklin you it's you what really i didn't have this on my calendar well i'm sure you can just present right off the top of your head and i'm excited to hear about application state i've like run into problems with this where you start like building up state in a component and then you're like stuck when you have more than one component and all that so i'm excited to see what you've got to show great all right let's get going i'm ready so uh welcome everybody my name is carl franklin um i don't need to reiterate my bio here but you can just google me i've been around for a long time i'm an old guy i've also been involved with blazer since the early betas and i do a weekly youtube show called blazer train and you can go there at blazertrain.com and in blazer train i actually did this content but i build the applications a piece at a time so you can understand them a little better so i'm just going to show the done applications in this in this talk but if you want to see them built you know one at a time piece by piece you can go to blazertrain.com and check them out all right let's get going so uh what is application state so application state is essentially where you have instances of objects that are stored in memory and normally these would be you know in behind a page in blazer behind a component but sometimes you want to share those things and so because blazer is a spa framework spa applications are inherently stateful so it's it's kind of important sometimes that your application knows what all those variables are not only to be able to access them across components and pages but just in case things happen you might want to persist that those variables in that application state and then reload it later so what we're going to be doing is creating a state bag i'm sorry did i offend somebody with that estate bag state bag is just a class for accessing all the object references that you want in your application and the idea is that you get one instance per user so one user is not sharing that state bag with another user fortunately in dot net core we have these user scope services that we can create and if we put if we create a state bag class and we put all our public properties and all that stuff in there then we can add that as a user scoped service or a scoped service which means we can share that between pages and components we can inject it into whatever page we want it's one source of truth and it also allows for stickiness so while we're talking about scope excuse me i have my breakfast while we're talking about scopes let's talk about uh the different scopes that we can have in asp.net core we have per user instances and this is what we're going to use for application state bag so think session in web forms and the only reason i'm bringing this up isn't because of the technology behind session but the accessibility and this is it was very popular in web forms to have this session object that we put stuff into and take stuff out of while the user is online so we don't have a session object thank god in asp.net core but we do have the ability to scope any object to uh you know to the user so this is a per user instance now a per application instance we have these as well and you can think uh the application object in web forms this is a good place to put static data data managers helper methods anything that doesn't require per user access okay we have those as singletons now singletons we'll talk about that because in a blazerwaza map there really isn't any need to do a singleton versus a scope because there's only one user but then we have just your basic class instances okay so class instance is essentially transient and that is essentially what's going on there so if i create something at the uh the top of my page or whatever that just lives with the page the counter is a perfect example of this right you move off the counter page you move back to it and that counter has completely reinitialized so you can add these instances with scope in startup cs for blazer server and in blazer wasm in program cs so in service services cs for blazer server we have in configure services you can add transient right so this is for that class level and you would do this instead of just creating a class level variable if you require dependency injection so if these classes have to be set up with dependencies before you use them this is a great way to do it ad scoped is that per user scope every user gets their own instance and ad singleton is the application scope so in blazer server one instance is shared among all the users and in blazer wasm it's the same as a scoped service because there's only one user you're not going to have more than one user it's running in the browser right so let's just dig into these for a minute this is sort of repeating what i just said but transient scope is the default for component variables it's re-initialized on navigation it cannot be accessed by other components and if you want to add dependencies you use add transient the user scope is services ad scoped of whatever it is it can also have an interface in front of it so if you want to add it as an interface and then the implementation that's good you get one instance per user and this exists outside pages and components and can be accessed by other components just by injecting them into the pages and components so this is perfect for application state and this is what we're going to use application scope add singleton it's blazer server only obviously you can add singleton to a um to a wasm project but it's going to behave the same way because there's only one user okay and this one instance is shared among all users so if you look at the default template the um weather forecast service is implemented as a singleton because it's just creating some dummy data and it's not there's no state there right so this is a good place for static data methods so the downside of transient data which is the default is that multiple copies of the same data uh are used across data page across pages so that means that every page has their own instance of this data and one page cannot see another and so think of a situation well i'll get to the situation but synchronization headaches this is what this will cause and it's not one source of truth so here's the scenario you have a toolbar component right across the top of your page that stores lists of objects that are used in the current page whatever that page is all right so the user maybe selects an object in the toolbar with a dropdown perhaps and then selects a button to load an an edit page below the toolbar which then the user edits so editing the page uh retrieves the selected object and allows for editing so if the page isn't using the same object that the toolbar is using you've got two copies of that selected object existing and now you have to synchronize those things and it's a pain and we shouldn't be doing it so the solution is to move those objects into a state bag a scoped service where they can be accessed both by the toolbar and the edit page oh my coffee is here hello i'm talking with the world right now can i say hello to the world sure kelly ladies and gentlemen after i pour the coffee of course quickly because i only have a half an hour but hi here's my uh like the one who must be obeyed thank you all right so the solution again is to move those objects into a scoped service a state bag or app state where they can be accessed both by the toolbar and the edit page so there are three ways to implement a state bag one is the easiest way is with a cascading component so the app state itself is a component it's not a service it's not something that you inject um it does not allow for notifications even though the ui updates now what i mean by that is when you change a property on an object in a cascading component in any page any other page that is bound to that property is going to immediately update however if something like the toolbar or or a page or or some other component needs to know needs to get control needs an event handler when that happens you can't do it with a cascading component as far as i know so the next way to do this is with a scoped service so with a scoped service you can inject it into any component that requires it as i said as a ad scoped service and it's useful if you need to raise these notification events but it does require some event handling code and therefore implementing our disposable in any component or page that needs to handle that event now there's a hybrid approach and i think this is really good because it is a cascading component around everything but then the app state is is a property of that component and we can still get control if we want to get event do the event handling wherever we need it but we don't have to and you same with the scope service you don't have to handle the events if you don't want to the events aren't for updating it's it's going to update because it's bound properties will update automatically this is only if you need notification all right so for the cascading component method we're going to create a razor component i called mine cascading appstate of course you can call it whatever you want you add public properties to be shared across components and then you enclose the entire router in appraiser with this cascading app state and then anywhere you want to use it you declare it as a cascading parameter all right so let's look and see what that looks like here let me pull up my demo everybody see this okay i hope you can so this is just a blazer server application it'll work just as well in a blazer client application and this is called uh app state via component and all of this will be available to you later so what i've done in my shared folder is i've added a cascading appstate.razer component and let me see if i can oh no that's right all right so you see this cascading value up here uh and wrapping a child content render fragment so this is an easy way to say hey we're going to enclose this component around other things this is the other things right there that's a render fragment so that's anything anything in the app at all in fact it's going to be the entire app now we have three properties and the reason that i have these broken out like this is because i want to call state has changed when we set any of these values so i've got counter i've got message and i've got enabled that's it so in the app razer now i have completely wrapped the entire router in this cascading app state so let's look at the toolbar and yes i have created a toolbar component and this just has a div with some styles in here and if app state i'm sorry here's our cascading parameter cascading app state right there and just by doing this we get access to it so if app state enabled we're going to show a button with you know the success class enabled disabled so that's cool and then we have this other button update message button clicked to update a message and we're going to set the app state message to message updated at date time now too long time string just so that we know that's happening now my main layout razor i've replaced uh this default about thing here with the toolbar so the toolbar is going to go at the top of the page now in the index page get this i also have access to that cascading parameter the app state and i've got two buttons one to toggle enable button clicked and the toggle toggle the update message button clicked so this is just exactly uh no never mind that so two buttons one to toggle the enabled property one to update the message and that's all we're doing is we're just changing those properties on app state okay so let's run this puppy and you'll see what i'm talking about here oh remind me to tell you about child component i'll show that afterwards all right so here's my toolbar right this is not an enabled button this is just a button that's showing enable disabled from the toolbar i can update the message now this message is on the app state so i'm showing it here and i'm showing it here right anytime i update that it updates now i can toggle enabled from the page and because it's also bound in the toolbar that shows there toggle enabled i can also update the message from the page and i have a child component that can update the message as well and let me show you that child component it's very simple i just have the cascading parameter app state in there and i'm just setting app state message equals updated by child component that's all i have to do all right let's talk about using a service so to use a service we're going to and what i did you can knock yourself out naming wise but i created a class called uh app state cs in the services folder and i added a public event for change notification and a private method to raise that event and now app state properties should be defined with private setters and then each property has an update method that sets the value and raises the notification event and that prevents callers from circumventing the event system so uh in blazer server i'm going to add it to services.cs as uh services.add scoped app state and in blazer webassembly i can do this in program.cs builder.services.ads scoped so in the components that use the app state i inject the app state service i implement idisposable i handle app state state change and there's two parameters a source and a property name so the source gets set by whoever raises the event this is my component and the reason i do this is so that i can check to see if it's not me before i act on it because i'm handling it and yet i'm i may be raising the event but i want everybody else to get the event not me so i test that source to see if it's this it's not this and i inspect the property name take appropriate action now remember notifications aren't ui updates ui will update automatically notifications are for hey i need to know when somebody else updates this property so we're going to hook the event in on initialize and unhook it in disposed so let's see that demo so here's my app state and services i want to keep an eye on the time uh app state and services i have my message enabled encounter with private sets and then i have an update method for each one and then i'm going to call my notify state changed okay here's my state changed event here's my notify passing the source in the property so here's my startup i'm going to add that as a scoped service in toolbar i'm injecting the app state and i'm implementing idisposable all this ui is the same but instead of a cascading parameter here i've injected app state so on my update message button clicked i'm going to call app state update message this because that's who the component base that updated the message and in state changed i'm gonna if source is not this that means somebody else raised the event i can inspect the property whatever it is see if action needs to be taken and here's where i hook and unhook the event now this is going to look exactly like the last one except that we have this added layer of functionality where we can figure out who raised the event all right so it's going to look exactly the same here's here's my component all right let's get back to here all right so the hybrid state bag uses the app state service as a property of the cascading component and it allows for persisting the state bag now why persist you want to restore the state of the application between browser visits so one scenario filling out a long form how many times have you had to fill out a form and then it asks you like when you're almost done you know what was your median income in this year and you have to go look it up you come back and it's timed out if you have a network outage you accidentally close the browser that kind of stuff so on the client we can use secure local storage but the and this is new in dot net 5 and i'll show you that you can use local storage too i would recommend you know if you can use secure local storage that's great but just think about not storing secrets that's not a good idea in general so you want to store just like the basic stuff that you're looking at as long as it's not sensitive data you get five minutes five megabytes of a limit of data per url with local storage and this is what we're going to use on the server on a server of course it's not a you don't want to use a shared data store but you certainly can persist it to a database the downside is it requires a network connection so here's the app state persistence pattern we're going to choose a component that's always available like the toolbar the nav bar we're going to access the cascading app state provider we're going to hook and unhook the same way we did with the service and on uh the state changed we're going to persist the app state now you only want to do this in one place that's why you got to choose one component we're going to use the javascript wrapper to local storage and then i'm going to show you how to use the new protected browser storage in a blazer server app and yes it only works in the blazer server app so here's my hybrid and just about everything is the same here this is the cascading app state parameter here's my child content my app state as a property i've got a key for local storage and also check this out i've got two functions get local storage value which just says local storage key equals value and set and get so this just wraps the javascript local storage okay so back here what i'm doing is uh i'm on parameters set async which means i'm reloading the page i'm getting the local storage value and i'm deserializing that with the json serializer and i'm setting app state now i have a save changes which is not async and i have a async save changes and so if i'm saving changes i serialize the uh app state to json and set local storage now this is the clear text version so let me show you what that looks like and why it's a bad idea in local storage bad bad idea all right so here i've got my tools up my application local storage right here i'm going to update the message and now you'll see in clear text look at that message updated it blah blah blah all right anyone can not only can they see that but they can change it like anybody with the browser tools can change this value message not updated and then when i reload they've actually changed my application state so that's not good okay but let me just get rid of all of this stuff and show you that everything does work the same way as it worked before all right let's switch over to using the new and shiny protected browser storage and you can see i've injected this uh and this is a.net five application obviously so let me get this out of here i'm changing my key to app state json protected i will comment this out and where did my comment toolbar go that's great view toolbars edit where's edit text editor all right here we go we will now use the new.net5 protected local storage it only works on the server and a moment's thought will make it clear why the server is going to come up with the uh encryption algorithm that stores that for you and so therefore you do not want that on the client you don't want the client mucking around with it on this side when we save changes we're going to use protected local storage as well and this is all there is to it like it's local storage get async we're getting a string we're getting the key we're deserializing that and over here when we save it we're setting that json with that key with protected local storage let's run this and see what happens again we'll pull up the uh the tools look at local storage here's our our clear version that was there before now when we update the message now here's what happens if i wanna if i try to change any of this and i reload it yep it just creates a new app state so basically it's not uh it's it's not messable is that a word messable all right there you go so kudos to the.net team the asp.net core team for getting that protected browser storage working so in summary uh managing application state is really necessary in a spa because spas are stateful you want to move required references into a state bag implement app state as a cascading parameter a scope service or a hybrid of both and to persist use the hybrid model because you get the best of both worlds now i just want to call out blazer train and this is me shouting at a passenger on my train because they're uh heckling me from the sidelines if you go to blazertrain.com there's a whole bunch of free content there and i do a new show every day and i also want to mention that devexpress sponsors blazer train it wouldn't happen without them so thank them next time you see them and thanks for joining that's it for me back to you guys wow carl this was great i think i have time for a few questions here um okay so uh so one thing here that came up was this uh is there a way to programmatically check to see how much local storage has been used yes as a matter of fact i did show that i don't know if you saw that but here app state size 75 ah there it is okay that's just essentially when you serialize that string you just get the length of the string and that's what's saved okay that's awesome very cool uh another question here and kind of related but is is um you know how are secret stored and you were showing app state but i think you kind of want to one thing is the whole kind of encryption thing but you also want to kind of not be sending state down to your clients as much as possible right well yeah so when you save state in local storage what happens is uh it gets it goes boing boing boing right so here your app updates it it goes to the server and then goes back to the browser which persisted in local storage and then when you want to read it it has to go back through i mean if we're talking blazer server anyway it has to go back through signalr and then back down to your to your client right so i think you know five megabytes is the limit for a reason that uh you shouldn't be storing a whole bunch of stuff in there and it's my thought that you probably shouldn't be storing well you definitely shouldn't be storing anything that you don't mind the user messing with and you saw how easy it was for me to to mess with that local storage right so if you're going to if you want to you can persist it on a data store you know just with a regular api call or something like that and the downside again of that is that uh you know you you have to have an internet connection and sometimes state is useful like filling out a form or whatever state is useful when there is no internet connection right connection goes down very cool well that's all the time we have this was this was an amazing overview and really good information so uh so thanks a bunch carl all right woo-woo
Info
Channel: dotNET
Views: 11,325
Rating: 4.9051385 out of 5
Keywords: .NET
Id: GIupo55GTro
Channel Id: undefined
Length: 29min 43sec (1783 seconds)
Published: Fri Nov 13 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.