Re-frame your ClojureScript Applications

0 0

hi everybody my name is Sean Mahad and today I'm gonna be talking to you about reframe which is a closure script framework for writing single page applications and my goal for this talk is to give you enough of a background and enough knowledge that you can get started building your own applications with reframe right away you'll still have to go back to the docs and things because it's only 40 minutes and I can't cover quite everything but should be a pretty good start at least so instead of showing a presentation I thought it would be a little more fun for us to build one runway there we go so we're starting with a blank closure script file here and I've got all the CSS built already so we don't have to deal with that and first thing we're gonna do is just add our name space and a few dependencies so we have to include reagent for anything that's going to be rendering views because that's what it that's what reframe uses for all your UI definition and we're gonna include reframe because it would be a terrible reframe talk otherwise and then there's a library here reef risk which is relatively new and immature but the developer has been doing a lot of great work with it and it's very easy to add quick visualizations and then spec because I went to the workshop and it's kind of fun and then we have this as views as well that is just a little bit of helper stuff I don't want to throw in the same file and I'm gonna I'm cheating by using cursors live templates or IntelliJ is live templates so we're gonna start by defining a few reagent components so we've got our main one which is this slideshow and then our title slide and a vector of slides that we can move back and forth between and if you've never used anything with with a hiccup markup before you know it pretty much as HTML you replace your angle brackets with square brackets and kind of squint and you can pretend you're just doing normal web development and we need to add sorry we just need to add something to render to our screen so we use this reagent render method and it takes any valid reagent component and if i refresh the page we get our first slide up here but I hate having to refresh the page so we're gonna put in a fig wheel with this on j/s load hook it's all built into the project clj and this run function is what we're calling straight from our HTML so if i refresh it again we've got now live updates with fig wheel and I've added this reef risk into it and we're gonna keep going with adding some of our views I guess so I'm going to add this footer for our slides and then we're gonna replace our title slide so that we actually have some sort of controls and let's see I just have to reorder things a bit and is is the the code on the side is that big enough for everyone yeah perfect so now we've got these forward and back buttons but they don't do anything I've got them hooked to these reframe dispatch functions but we haven't built those yet so we should probably figure out what we're doing so I'm gonna add an extra slide here and just replace this with our intro slide so we've got our sort of classy marketing spiel here but I'm not a huge fan of marketing so I'm gonna cheat a bit here and we're gonna add a bunch of slides down to the bottom that I've already prepared clean up some of these let's see what's duplicated this is about my favorite thing about the new fig wheel because I make a lot of mistakes when I'm building any way out and since we don't know how to actually use reframe yet I'm just gonna cheat some more and start moving through the slides manually so when we're building a single page app mutation is fundamental to everything we do we have to change our Dom change our application state and most of the time we want to interact with the world somehow so we're gonna be changing things on our local machine like local storage and cookies in the browser and then we're gonna be making calls to api's and databases and particularly when we don't control those this is like a nightmare of you mutation and we have to build that into our app somehow but we also really want to program functionally so we want to be changing our Dom functionally which thankfully react was invented we can do that we want to change our application state functionally and we have all the power of closure to do that and changing the world functionally is a little bit trickier so we need to move all that mutation out to the very edges of our systems so when we're building what our business or our clients want we don't have to think about it all the time so we're gonna look at sort of the basics of how reframe works and primarily how do we get change moving through our system so reframe is an event based system and if no events are happening the page is just gonna sit there so what we'll do whenever an event occurs we're gonna dispatch or whenever an action occurs so whether it's a user clicking something or something returning from an API or calling calling our set page we're gonna dispatch an event and there's a couple things to know about events in reframe one of them is that it is the only way you should be changing anything in your system if you start mutating things directly it it's going to get more and more unpleasant as your application grows and the other thing that's great about event dispatch is we're dispatching data so all we're gonna send is a vector with our event ID and whatever arguments we want in there and so that gets sent into reframes central event router which is then which is sort of got an asynchronous first in first out queue and when it goes to actually process that event it's not going to execute any of the effects you've told it to do so if you're telling it to update the database it doesn't immediately action that it's going to return a map with keys in this case it's gonna be a database key and it's gonna tell you what you what effect you want to have on it so again when we run these these event handlers we're getting data out the end and we do have to realize those effects at some point but reframe handles that all for us so it's gonna go how whatever whatever you've defined or the system is defined for you for how to execute those effects reframes gonna do it in the background you don't really have to worry about it and so one of the expectations is after those effects are realized most of the time your application state will have changed so we want a way for our application state to go and change our views and reframe does this all reactively so we're going to define some queries on our app state to say this is the part of the data that I care about and whenever the application state changes those query functions are going to rerun and they're gonna push out some more data they're gonna compute our reagent views and update that hiccough data so again we're not worried about actually executing any facts even inside our system those computed views are they going to be pushed by reagent out to react which is going to realize those changes and move them into the DOM and our system then is back at rest and waiting for the next event to occur so to get started writing a reframe app we have a few things we need to do so we're going to define our views which we've already done a couple of we want to define the initial shape of our application state and this is one of the places that you have a lot of leeway but as your app gets bigger you can start adding things like spec into the initial app State so you can make sure that everything is well-formed you can also in this step go and pull things out either maybe you're pulling something from local storage or calling some other API to populate your initial data after that we're gonna register the events we want to run on our on our page and the queries we want to run to populate our views then last the last two things we kind of do are just that final plumbing so we're gonna link those queries into our views so we can populate them and link the events so that our users can actually do something so we're gonna do here we're gonna add in our initial application state so we're going to set it our current slide to zero and we're gonna start registering our events so this first event you'll see it we've defined we've got that unique ID which is a name spaced because this was I kind of clean these up after rich and stew stock so now everything's name spaced I think and and then your function takes as an input context and we don't directly supply that context it occurs as a result of what's in our system and what we dispatch in our event so if I were to run this right now nothing happens I've no visibility on what's going on and I'll zoom in a bit on here so we can see in in reef risk it gives us this automatic view of our app DB which is where all your applications data is stored but we can also add data to it so we can do kind of the sort of sprinkling our code with all the debug statements we want so if I addy add this in here I'm just gonna add some data which is going to show us what's in that context and if we expand this we can see it's getting in this DB key which is your application database sorry this is a difficult to move around but and then it's also getting our event vector which we've just passed in the ID so because we have all the power of closure here we can D structure this and give it a little better of a debug view see I might have messed something up here but we're gonna try and dispatch this again there we go so now we've got our context broken up into this event and DB as well which makes it a little bit easier to deal with and if you've used the previous reframe this is essentially what was passed into the old versions of registering the event you just got your database in your event keys and what we're gonna do here now is add some output to this I'm just gonna get rid of the old one and so what we've done what I've done here is I've added aspect to say is my current slide in integer because that's what we need and then if it is we're gonna pass back a map of what we want our new database to look like so I'm going to take whatever the existing database is and a so sin this current slide if the slide number is less than the total count of slides we're going to increment it and if not we're just going to return what we had and if speck returns an error we're gonna just display this this error message so if I were to dispatch this right now you'll see my application state has nothing in it so I get this speck here but well I get this thing I'm calling a speck error but it's not actually a speck error and tells us gives us this message it's not a valid and so we need to initialize our data in order to actually start working with it so I'm just going to register this other event slide to initialize we're going to D structure it so we're just worried about the database and we're just going to replace it with this initial state and so we've gotten our slideshow current slide is zero if I rerun this you'll see it now increments I don't get my spec error anymore so we know our data has initialized successfully all right now what we're gonna do is just replace this run command here so that along with certainly setting up a reef risk and mounting our route it's also going to initialize our or application state so i refresh this and we zoom in again it's all there so we've got our next slide event I am gonna want to go back again in case we go back and forth so I'm just add a matching a matching event to move to the previous slide now we can now move move back and forth in our app dB and we can we can do this as well with our buttons they're all wired up properly but our views aren't changing at all so we need to get those subscriptions in there and link them up so I'm gonna register a subscription that just queries our app DB says give me the key that matches this slideshow current slide and return that value and again this subscription is returning data so we don't you know you can do whatever you want with it and this is um this is probably my favorite part of reframe is that every single step through the system you've got a function you're returning data that data goes into another function and and there's nowhere where you really have this uncontrolled mutation which when I do this myself I usually cheat and I'll have a couple functions here where I was too lazy to make it really nice so we're gonna come in here and start wiring up this our slideshow of you there we go so I'm just gonna replace this here and you can see all we have to do is dereference a call to reframe subscribe we'll pass in the subscription ID that we want to return and this is another area where if you've used the previous version of reframe before 0.8 this was not really a thing you could do the developers have taken it and they've they've kind of really revamped the subscriptions under the hood so now they are really are now a deduplicated signal graph that instantiates the only when you're looking at those particular subscriptions so it's it's kind of cool particularly because it means I can be a little lazier when I'm doing things so when we look at it now we can actually move around in our application and what we want to do now is start growing our app larger than this starting prototype and there's a few ways that reframe gives you that make it really nice to sort of decompose things and then put them back together so that you're always working with something that is understandable and all your cross-cutting concerns can be moved out into other areas and again because it's all data you can test those all those portions as much as you want in different ways there's as well it's all written in C ljc and one of the companies who's using it heavily has really invested in the testing tools so they're testing all of their events and I believe all their subscriptions using closure not closure script so the runs a lot quicker you don't have to deal with anything you know that is try trim okay I can't remove the name of it but some of those other testing libraries for web apps are not fun so the ways we have to grow our application one of them is with Co effects and it took me quite a while to understand these but they're not difficult so all it is is if an effect is the thing you are doing to the world a co effect is what that effect needs in order to run so it would be whatever parameters you've got you can pass them in through this Co effect and this website is really good he does not sure how to pronounce his name but he does he's doing his PhD and parts of it on Co effects and one of the really interesting things about it is you can you can sort of inject things about the world so your function never has to worry about them and and one of the examples he's got on his site is GPS coordinates so you can pull them from your phone your app just has them there when it starts the other side of that is we can define our own effects so the only effect we've used so far is this database effect which updates the state of our database but there are there are other built-in effects and there's effect libraries already available for reframe and one of them in particular is for HTTP requests so rather than having to build your request writing to your functions you simply add this this key HTTP effects you pass it in some data and it does all the work for you and I've done some stuff where I've then further wrapped that with my own effect so I don't even have to pass in my API location or anything like that and it makes it really nice to sort of just decompose all of those concerns and the last one that we've got here is interceptors which are very similar to middleware but they give you a little bit more power so we're going to look at how we use those and let me go right here so what we're gonna do here is I'm gonna add an interceptor that does my logging into reef risk for me so we pass it in and I an ID which is optional but it is kind of nice and then and then we build a before function and this is going to run before our regular event hat Handler and we build an after function and both of these are optional so you can do one or the other and so what I'm going to do here is just when my before handler runs I'm gonna pop out my context at that point and when the after function function runs I'm gonna pop out the contacts there all right so I moved back now and so what you can see is that in here somewhere there we go so I've got this event log which it's it's popped out my event ID previous slide and my before I've got you can see my interceptors can see the co effects which is our context you can see the co effects that were there before that funk that handler run and then it's also got two things in here we've got a stack which is all of the handlers or effects or whatever Co effects everything that's run beforehand and I'm not sure of the particular order in this view but you can go through and you can say okay what's already happened do they have a before or after function and easily find that in your code then we've also got here a queue of things that are gonna run after this particular handler went so it's very nice particular or I use it generally for logging but you can also affect those things in your interceptors so in our before interceptor we can mess with the context and we can inject more things in we can pull things out you know you can doesn't mean you're gonna always make good decisions about it but at least we have the power to do that and what I'm gonna do here as well is we're gonna look at oh and sorry I didn't I didn't cover this part but if you look at these functions now all my logging is out of here so my event doesn't have to worry about logging except for adding this logging interceptor and if we wanted to add that through our entire system what we could do is just wrap the event registry function and add that interceptor to everything we called and then run not in our development mode with whatever logging or a testing we want pull it out in production and you're just running happily so I'm going to add another interceptor here oops oh there we go no I'm I'm just mixed up about the order sorry what I'm gonna do first is I'm gonna change my initial state here so we have this other key to test with because if I if I mess with this current slide nothing's gonna render to the page so this is kind of my lazy way of simulating a screw-up so I'm gonna just replace these events again and you see now I'm just passing in two interceptors so we're we're gonna move all of our spec checking out there and not not have to worry about it in our event and so so now I've got in here all of my spec handling and if I if I run my run my application and and it's working you can see I get this I get a spec here but I shouldn't I think that's just a bad definition on my side but what I can do is simulate in here I'm gonna look at my broken slide instead and this should give me a spec error for sure really all right I might have to just you might have to take this on faith or I can show you after because I made a mistake here though an essentially but essentially what it should be doing here sorry no the ID doesn't matter on these I think I I must have just in copy and paste mode before the talk messed it up but what it what you can do here is I can I don't know I'm gonna do is I'm negative I'm gonna run my I have a complete application here that should work so when the when I'm checking this speck I'm gonna I'm gonna switch it to this broken slide here and when I try and move here all right sorry about that um what you can do essentially is when I run into this error I can clear my entire queue here so along with sending back whatever messages you can also say none of the following events are going to run at all so it gives you a lot of power to say how do you want to handle errors do you want to kind of just warn do you want to log it do you want to stop the whole system and the other thing we're gonna take a quick look at here is how do what we can do with Co effects just can get our slideshow back so so there's this demo application on the reframe site which is doing a to-do app and one of the and it's it's got a pretty good set of specs in here and if you're interested in that part you can take a look and see how they've done it what it what it also is doing is it's storing to local storage so if we want to declare our own Co effects what would do is we do this register Co effects and here he's just reading in the - dues from the local store and you know in the in the function where we're actually setting those up you can either put those Co effects as part of an interceptor or you can find it here yeah or you can just inject it directly so they're they're injecting these Co effects and then also running the spec interceptor and so that's essentially all you need to know to get started with reframe there's tons of stuff on the docs I they're good videos on lambda Island that he's recently released purely functional TV has one kind of one hour with reframe and if you're if you're coming from the world of reagent most of what you're used to it's probably doing that stuff under the hood so you don't have to worry about how you're wiring your application together you can still build your reagent components with local atoms you can put cursors in if you want and do more complicated local stuff but you really want to be using reframed for all of your application level organization communication everything along those lines and that is all the material I have for today so thank you very much for coming to the talk and I'll take questions after [Applause]