ElixirConf 2021 - Michael Crumm - Getting to Know the LiveView Lifecycle

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Applause] [Music] [Applause] [Music] uh hi everybody welcome back uh very happy to be here with you today my name is michael allen chrome jr but please call me chrome i'm a phoenix core team member creator of live dashboard uh contributor features fixer bugs and today we're going to be walking through discussing and getting to know the phoenix live view life cycle so let's get started i've got a couple of small short vignettes i wanted to go through with you today to kind of discuss some of the different stages of the life cycle so let me bring that up now so the first one we're going to talk about is mount and well that's fun um okay so let's see what we can do here let's see if we can fix it uh we've got an undefined function error at get slash or the roof path function interweb dot page controller dot live is undefined or private okay i'm confused interweb.page controller sounds like a controller but the live function really makes me think this is trying to trying to call a live viewer or invoke something so i'm really not sure what's going on let's let's go take a look at the router so if we pull up router.ex we'll find all of the paths that we've defined for our application and here we can see a big group of live paths but this first one well something doesn't seem quite right if page controller is in fact a controller and let's check that first it is then we don't want the live macro here so interesting point about live view is that uh all live requests start the same way as a regular stateless http get request uh because it is in fact an http get request but for page controllers we need to use a different macro so we'll we'll switch this back to the to the get macro and try that again wonderful okay here we are so welcome to the internet and and my talk on the the phoenix labview lifecycle let's start by having a look at the mount o well the i guess we'll take a look at the source the the mount stage of the life cycle is the very first thing that occurs when you make a connection to to phoenix live view it mimics the a stateless http get as i said and it invokes this mount three callback in your live view similar to a controller action but it's only the first stage of the life cycle after the mount follows with a render and as we'll find as we go through all of these stages every stage of the lifecycle effectively falls down into a render that assumes however that the data changed and that talks more about the the diffing engine and how we actually send data you know or or the the structure of the data that we send to the front end when you when you are you know operating your your live view i'm not going to talk so much about the engine today but the mount function leads to the render function and that is where we actually get our html output but this page isn't outputting my demo it's it's outputting something but that's not what we wanted i think maybe let's remove this because yeah here we are okay so nifty thing about live view is that you can co-locate templates that template was rendered in line it's not actually what we wanted the the co-located templates on the file system and it has the full demo in it so here we are all right and we can we can continue moving forward so as i said stateless http is where we as our as our humble beginnings but then we are able to upgrade and when we upgrade we find you know that we have available connection information like the user agent um so the thing about user agent is it's pretty worthless but it's sort of necessary for my demo so i guess we should probably we should probably fix it the user agent comes through a stateless http request as a a request header uh and that's where we we can grab it from when it comes to the live view if we look at the mount we see a couple of different functions being invoked here that are specific to live view and a couple that are specific to the mount callback itself assign new which is used to take in the signs from the initial connection that would happen you know in a controller what you're very familiar with if you use phoenix at all but and also live view but we can take the controller signs and we can sort of impute them onto the socket assuming that they are there this is what assign new does so this assumes that you know if there is a user agent value already on the socket or i'm sorry on the con then it will be added to the socket however if it doesn't exist this callback will be invoked and will be expected to supply the value for the connected render important note the disconnected and connected renders of the live view lifecycle are completely separate so consider a distributed such you know distributed erlang environment where you have multiple nodes you're making separate http requests so the connection the the state excuse me the stateless http request that is made for the initial amount the dead render if you will is not guaranteed to arrive at the same server as the one for the socket connection the initial socket connection which itself is stateless until it is you know upgraded uh via the websocket protocol so all of that said here we're trying to assign new and we're using a special function that is only available in mount called get connect info now this allows us to pull information off of the socket connection that stateless connection for the socket when it occurs i'm sorry the stateful connection for the upgraded socket when it occurs and for that to happen it lives uh we have to define the data that we want from that connection on the the socket definition itself so for that we need to pop over and take a look at the endpoint and the end point shows us our socket live where we also set up our see this is where the session information goes it's also in the connect info and this is the part that is shared between live view and your plug session further down in the in the endpoint and that's how the that's the security model right the shared uh request key that we the token that is used for csrf protection and you know just as a security measure so here is where we load this connect info i see user agent that doesn't look like what we have though ah let's try this user agent okay that i think will be better and yes fantastic we have a totally worthless string that tells me lots of things about my browser don't trust this information folks um the next step of that is that we should also pull the browser language but we didn't i'm sensing a theme so language comes from connect params can params are decidedly not connect info they they don't come directly from the socket they are specific to live view in that they come from the live socket connection that we make so if the data from that is coming from the client there's about only one place it can be and that's going to be in our javascript so let's take a look at app.js and see exactly where this is happening hey what do you know params right here off of the live socket so params is a map or a json object here and in this case we're sending through language ah language that's better okay so now seeing where we can make a stateless connection and get a render from the stateless connection and then an upgrade to a stateful connection containing data if i do a quick refresh here we'll see that there's a brief flash of of unloaded or unavailable content where we're not pulling the uh the user agent from the initial con for for the dead render if you will that's something that we could correct from the interest of time i'm not going to do it right now but if you're interested reach out to me and i'd be happy to talk to you more about it so uh let's move on to our next example and we can talk a little about navigation so navigation is a place where live view starts to diverge a bit from what is available in a uh you know in your general like dead connections or or dead renders the stateless nature of http is that you make a request to the server the server moves the world to serve your needs and then responds with some data and that data is on your page and anything you do after that requires another connection back to the server in the case of a live view given that we have a stateful connection already we have options when we go to to move around the page perhaps we want to stay where we are and just update our our state as it as it exists or perhaps we want to leave or even reload the page where we are but we want to do it with some fresh state these are our options and they come in the form of push i'm sorry patch and redirect so think of redirect as any standard link right the inter the interesting part of live redirect will become apparent a little bit later when we look at live sessions but a redirect is effectively a link a patch is special to live view it's something you can only do here or with you know systems like ours that where we are we are maintaining a process on the server that is holding that state for you and the patch actually updates or patches that state so handy table to kind of discuss that here's my little demo we've got some links that will patch in a color and increment the value and if we redirect they'll reset the value and they don't okay what about reader well it's it's doing the redirect because i it also did the scroll for us um but it isn't and it's patching the url so okay so well here's so here's the workflow uh we do we do a mount and mount then leads to a render we do a handle params as well so the the navigation step invokes this handle params callback in between so we have mount handle params render handle params is invoked uh instantaneously or immediately in the calling process but uh when when you do a patch because it's live there is no additional step to take there's nowhere else to go there's no new process to start or to shut down we are simply passing the state where we live so handle params is invoked immediately and i'm implementing a pattern here that you will see in the phoenix generators where we apply an action and that allows us to sort of uh get some some better symmetry with the way we we operate our socket so we move the socket you know to the beginning and we give ourselves access to the action easily and then pass the params along and we can see that we're setting color and we've we've protected ourselves we don't allow arbitrary input we only take a few colors and we update with the things and i know we're getting to here because i saw the url change so we we definitely patched when we apply our action we okay so name well so these parameters are defined because it's just a url path that they're not queries and parameters so it must be defined in the router i'm going to jump back over to the router let's what did it say about well that's interesting okay so here we are uh color calor uh if we we find our way back here and we well okay that's always fun that's an extra bit of fun i can only assume that my terminal crashed in the x code okay well still not it's not quite getting where we were wanting to go so let's take another quick look and we said that we needed to change this value to match the parameter names in the route and the path in the router hey and what do you know it works and we have super atrocious rgb but we are incrementing and we are patching and that looks great and now let's take a quick look at yes and our redirects are working as well and we can see that they find their way back to the mount where changes gets reset to one but then we find our way back through the call to handle params and apply action which will increment the change to uh sorry it's set to zero and then we increment it to one okay now where are we let's jump over and look at events i am you know it would just be swell if this one just worked so events use the changes and they were using a handle change callback which will fire uh handle change on a on some form elements we can take a quick peek at that over here and see that yes indeed we have a form and it has a phoenix change change color and that matches up with the name of this and these we can verify that the names of our fields are also color hey they're color and it's probably going to work and i'm going to guess that the internal parts are right so let's do a quick hey it works fantastic reds and greens and blues and we can see that we're even we're even patching the url here so we are this is the this is the handle event demo uh but you'll see that all these callbacks that you know they compose very well if we want to be good citizens of the internet then we want to make sure to compose our callbacks together and when we do an event that changes the nature of the page that we are on if we want to maintain our persist some state one place that we can do that is in the url and by doing so it means that we can you know i can take this link i could share it with you if you were a big fan that color and you could come and see it too okay fantastic it worked moving on let's do messages so messages are at the heart of everything we do here in elixir and is the basis for the you know the the just message passing nature of of the beam of of really truly everything that we do so i have built a demo here to send a message to an agent it will pair it back to us and well that didn't work at all um no function clause matching handle event okay so we sent a handle event some messages handle that send that looks that looks good what what did the what does the body look like chat message okay well uh aside from my typo of hi that that all looked fine so okay so so once again handle info handle event where it's the same flow uh we get a handle info handle info can come from anywhere anything can send us a message so we we get a message in this case we're sending ourselves a message it seems we're we've matched on a tuple module and then the message um we here's our here's our handle event where we're gonna send chat oh hey naming things is hard what do you know there we go okay send that that looks so much better okay let's try it again and i can i can fix my typo and i can say hi but well uh okay so the agent received unexpected message in handle info two and we got okay the message format looks correct this this looks like what i was expecting to receive to the code i guess let's what did we do wrong handle event sent so we here in mount in our in our mount we waited for the connected render with connected question mark and we started a an agent just for a place to send state it's just a demo uh we held onto the pin we assigned it to the socket we pulled it back out down we pulled it back out down here to update the agent process send after well you know what is interesting about an agent is that it invokes functions in its own process and not in the caller so self is not what we want so i'll tell you what what if we do this that's also not what we want because pid's not a function itself certainly is and so if we pin that or or just bind that outside then we can set the pit there and what do you bet we can get a message out now and there it is all right so those are my short vignettes um i have a couple of other things to that that is the live view life cycle now i want to talk a little bit about some of the newer things that we've been working on and what we've been adding to make it easier for you to build code that interacts with the live view lifecycle so let's first take a look at live view hooks interestingly enough you've been seeing hooks throughout the course of this demo so one place where i am using live view hooks is specifically here for the navigation this navigation lives in the live template for this or sorry the live the live layout template for this app so i've duplicated the layout i have a static layout and live layout that look the same in terms of the navigation i have a set of functions that we can look at here for generating breadcrumbs not traditionally a breadcrumb it's a nav menu but same concept sort of applies what is done and the reason that it makes sense as a hook is because we move the the actual logic into a centralized place so you have to re-invoke this stuff or you reinvoke it all the time you don't have to redefine it in in all of your live views so working at cargo sense where we do logistics intelligence and i do lots and lots of live view we have you know dozens of of live views or more that we are you know still even today invoking initialization stuff in every malfunction if you miss one it's a big pain because you know it's hard to track down problems so this is what mount hooks or really all the hooks uh it's a problem they attempt to solve specifically mount hooks because getting stuff into the amount you know the that initial kickoff uh is a little bit more difficult so we we wanted to make sure that was you know made made a little bit easier so first let's look at um the navigation this is a module just holding onto a couple of things and specifically i want to look at the the on mount callbacks so we'll look at where these where these get loaded in just a minute but the on mount callback is a is a defined by using the on mount macro in your live view or in your you know the live view macro in your in your web module uh or you know as we'll see in a moment in the router but from here we we when we define that name we sorry when we define what's going to you know what module we're going to invoke we can give it a an argument here to customize it a bit by default it is the atom default and we will this will get invoked prior to mount being invoked chris record talked about this a little bit in his keynote yesterday and this is this is the structure uh or part of the structure of what needs to be returned when you do this so if you're calling a mount hook you return either uh the atom c-o-n-t for continue and then the socket or you return halt and the socket to haul out of a mount you have to redirect so most of the time out of mount unless your intention is to redirect this away from the page you know you're you're going to be using continued socket here curiously what we do here uh and the part that makes this a lot of fun and i got to give a shout out to ben wilson at cargosense uh creator of absent and just all-around great guy and super smart when i started talking to him about the idea of life cycle hooks he said but what if we did it at runtime though and i went i was told macros and we talked about it and then i talked to chris and jose and the rest of the core team and they they really liked the idea and the more we played with it it felt like it opened up a lot more possibilities for everyone to be able to to to do things with with hooks so that said we do the attach and we attach another hook here in our on mount hook we attach handle params hook the handle params hook will receive the params and for you know it will receive the arguments for the params callback this gets invoked before your live views params all of the you know hooks come ahead and have the ability to halt the execution of the actual live view callback that's that's what halt is all about so in this params hook though we we continue because you know you may you may be needing params we're just here to grab some data so and in that way we assign new to the socket the current path which we pull from the url that came through params we parse that out grab the path and apply that to current path and we do that so that in our navigation component that actually generates the excuse me that generates the breadcrumbs or the navigation elements we have some code that looks like this and this is probably doing a very poor job of using the new slots api but i was just playing around with this a little bit last night and so i thought i'd see what it looked like uh so we we have our menu we do our our generation of each of the breadcrumbs this for breadcrumbs is link and for live breadcrumbs does a live redirect and it builds that out and where it actually in it's within this breadcrumb attributes helper where we do the path check to see if we are the current path and if so we set the aria current value and that handles the styling so that's hooks i'm going to quickly talk about sessions and i want to make sure i leave plenty of time for for questions because there's a lot here and to that end if there's something i haven't talked about because there's so much here uh if there's anything that i can help to make clearer or to that i didn't cover that you want to know about please reach out i'm on twitter at m chrome uh in the elixir slack at chrome there but you know anyway reach out i'm here to help um more than happy to to be a resource would love to be a resource for you uh on your journey in elixir so that said our last piece here just about live sessions so my very last demo is uh let's see we have we've been inside all of our live session and live view life cycle talk let's see if we can go outside it stands to reason so here we are we are inside let's see how exactly that goes down uh this is the live session defined by default everything falls in a default live session but when we define a live session it gives us the ability to add these additional callbacks such as an on mount list or stack of hooks that we would like to invoke some additional session data and so this is our inside route and then you'll see here as we go and look or our inside i should say our inside session so go down here and look at outside we see that there's a couple of other ones that are going to do some things redirect if you know if it was inside so basically looking for a session token we set on the initial dead render that you know put it into the session and then if you have it we're going to redirect you back into there so effectively once you've been inside you cannot go outside that said if you want to go outside go over here and i will clear site data and i can go there directly but alas once you've been inside you cannot go back outside anyway that's that's it for the demo side of things um i hope you enjoyed would love to hear your thoughts and and yeah i hope you learned something today thank you so much thank you so much chrome great talk you got a lot of comments in the chat about the style of this talk so congrats on that oh thanks all right um so there's i think we have a little bit of time there's a question about what editor are you using what editor am i using that was just vs code uh what you saw on the browser was called um what do i want to refer to that as procrastination is probably the best the best way to refer to it so the other hook and i will i will happily open source this code at some juncture and put it up on github so you all can can check it out um but the other hook that i didn't show that was in there was was one that sort of a two-parter it it has uh effectively it reads it reads the file and and stores it as a module variable uh or a module attribute and then reads that back out into a content editable element and when you save it sends a notification or a rather a push event back up through a client-side hook and then saves like re overwrites the file so like if i showed you my github right now you would see you know the the get uh i didn't get status you'd see a lot of unsaved files uh or uncommitted changes in in what i was doing so that was really unnecessary um but it was it was part of the great sort of exploring some of the other life cycle things for examples that was one of the things i was playing around with so yeah so that was the editor and then i think it's just like the monokai style i actually use the the makeup package in elixir to to do the styling of the code that i was like pulling in in runtime and and showing there so there's there's also please write a book about this and and um there's a couple asking if there's a repo where they can see all this code yeah um there there will be there will be at some juncture so i uh i'm working on a thing uh you may have seen it a little bit in there it was kind of at the bottom and i kind of i purposefully didn't refer directly to it but uh i am i'm in the midst of building a debug toolbar for the phoenix framework um i tweeted recently i tried to do a teaser i'm not very good at it but then again i'm not jose believe um to say that you know there's been something that's been bugging me it wasn't that i have a problem with the idea that dead views are dead to us that's fine uh it was that sometimes my dead views still have bugs so i'm working on a debuff toolbar it will work in for dead and live renders um it has some interesting properties that make it easy to see errors uh when they happen or to get you know more information out of your live view in a little bit more easily digestible way um somebody gave a lightning talk yesterday on something called ice cream and i would very much love to talk to that person so if you see this please reach out uh because you are doing things that i would love to be doing in the debug toolbar and so we should collab um yeah that's i will absolutely open-source the code i just gotta figure out uh order of operations great yeah there were like three questions about that oh there's one uh about i don't know where it is now okay so i approve it and i don't know if where is it now i'll i'll i'll make sure you you get it so you can answer it maybe in social media or okay somewhere that sounds good oh yeah here it is you mentioned that you were going to initially implement hooks as a macro making it compile time but you decided to go with runtime is there any performance or other downsides with this decision um so i'm probably not the best person to answer that question um i am i just in in the course of writing some of this talk had a light bulb moment about macros myself so i am now dangerous with macros look out but the um i i don't i don't think so the impression that i got was that it was more of a you know at first blush it seemed like that would make the most sense you know sure we'll make it you know it's something we can do we can inject some code you know it'll it'll just run and and no big deal and it was just sort of as we explored the idea of like but uh i think some of it may have come from so well certainly ben wilson had thoughts as i said about you know with his experience with doing absence and the sort of plug-like pipeline there for the middleware and things that i think he would like to go back and do again but you know we talked through some of the some of the different you know just things that you can do when it's run time it's a little bit easier than than if it is you know compile time specifically you can kind of you can kind of fuss with things i'm not recommending that you do this necessarily but i am i'm interested to see how people sort of uh i'll say misused but i don't mean that to say don't do it i just mean like i'm interested to see how people the interesting ways people come up with kind of trying to implement hooks or add additional things to things we haven't thought of yet because i have a very sort of straightforward line of thinking when it comes to how that stuff gets executed i'm excited to see folks sort of like break away from that and do something that we hadn't considered and see where that where that lands us uh so i don't i don't think that the compile i don't think there's a huge performance figure ultimately if i'm recalling what what chris mccord said to me at one point was like we have to invoke the function one way or the other anyway so um there's not really like there's not a lot of overhead to save and that we still have to maintain a list of the things you want called and called all right thank you yeah well i think that's gonna be all for now that's our time okay well thank you again everyone for coming and listening to all of this stuff uh and please do reach out if there's if there's anything i didn't cover that you'd like to know about thank you so much you
Info
Channel: ElixirConf
Views: 1,025
Rating: undefined out of 5
Keywords: elixir, liveview
Id: J1l4W0T7Jgg
Channel Id: undefined
Length: 30min 3sec (1803 seconds)
Published: Sun Oct 24 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.