Vuex? No, it’s X(state)Vue for UI

Bookmark

UI components are built on user flows or state graphs. To manage these states, we usually resolved to boolean variables system like isLoading. But this proves to be buggy and hard to maintain as the codebase grows. There is a better solution. In my talk, we will explore how we can fully control our app's states and develop a more adaptive UI system to users' behavior while keeping the codebase scalable with state machines, in real time.



Transcription


Hi everyone, it's very nice to be here. Be back to vue.js London for the second time. And how are you doing today? Well, I'm glad, I'm very happy and I'm even more happy if this conference is going to be in person in London. In a way, today I'm going to talk about state management for UI in vue. And that's not about vuex or Redux or anything similar. Well, instead we're going to talk about XState for vue and for UI. But right before that, just a little bit about myself. My name is Maya, as you also saw that. And you can find me on Maya Chavin on Twitter, on GitHub, and also on my website, mayachavin.com. I wrote articles about javascript and about front-end development in general. So feel free to check it out. OK, so as we all know, vue 3 was released last year and it's become a very, very promising update ever since vue 2. And we all really like how it works. I hope that by now you already have a chance to try it out or even migrate your application from vue 2 to vue 3. But either way, if not, then definitely you should check it out. Anyway, so what are the main things about vue 2? vue 3 has a lot of new features and breaking changes, of course, as always. And one of the nice things about vue 3 is the Composition api, right? Composition api is a significant improvement. It allows us to actually have more options and more control on how we want to build the component rather than the default option template that gives very limited in allowing us to write a component in vue. Now we can write more flexible and more dynamic components such as functional components easier. And for example, let's say if we have a component that what it does is just render an image when you click a button. This is how you're going to write it in vue 3 with Composition api, of course. So first of all, we have a setup method in the template. And now we can set a local state by using ref. It's similar to use state of Rea. So we can get a kitty with the direct with the default value is empty. And then we can assign a function to trigger when the user click on the button to update this local state to the image that we want once it fetched the image successfully. And then we render the complete return a render function. We will be written in JSX if you want to. So it's very similar to react in a way. So it makes react developer will be more easier to adapt to the new view. And the render components will render the components, the image component we want according to the local state and attach the event handler. Simple, right? Very straightforward. The output will be click, get the kitty. That's it. OK. Nothing very complex here, nothing at all. But let's say if the product manager is now decide to come and tell you, hey, when we click on the image, the image is still loading, but the button is still there. And user can click it multiple times. What are we going to do? We need to handle it by disable the button while the image is loading. Or if the image is not loading, it's in loading, we need to show the loading state. Because let's say a big image will take a long time to load, right? Or what happens if the image couldn't load for some reason, someone removed the image from the server? We need to display the proper error state for the image. And more and more. So at that time, we will probably end up with from a simple code like that with 20 lines of code to a component with more, extra more, like 10 more, 12 more lines of code like this just to handle two use cases, loading and error message, the very basic one. And then you also, what happens if you want to add more, let's say in loading, to the in loading 20%, in loading 40%, display a different animation effect, transition effect, display a different error state when error message, when the user, let's say the server returned 500, 404, something for the user. More and more use case adding, more and more problem will start to happen. And unfortunately, this is very, very common. We start with something super small and we've added and added and added more and more on top of this. That is called, let's say we build a component in the set of basic actions. And whenever we want something, we want to add some new feature. OK, cool. It works. We just need to develop something that works, simple. It's nothing critical here. But then when we want to add some new features, we need to add more, another layer on top of batch, on top of it with new actions. And because there's no code that is perfect, we still have bug fixes. We'll fix it on top of the current component. And then new actions will also cause some bugs. We still need to fix some bugs. And another feature come up, another bug fixes need to be handled, and so on and so forth. This approach is called a bottom-up approach. Unfortunately, this approach is very common, let me say. And it's not that bad. It works most of the time because it allows us to develop something fast, quick, and maybe dirty or maybe clean. But along the time, it's proved to be very, very complicated. For example, it's hard to maintain. If you have a code, a component that become growing, growing with more functionality added, more functionality added, and different more code added, it becomes hard to understand, hard to maintain. And then because it's hard to maintain, it's hard to test how we're going to make sure that what we test, what we wrote, will not break something somewhere that we don't know, we don't understand. I say sometimes we have a component that really long, then we have to read. But then if it's too long, we may not be able to understand all the use cases we're handling. And because of that, if something happens, we will not be able to understand whether we cover the test or where the step happened. That's hard to understand. And at some point, we will get to this one. And this is not on our side, developer side. It's actually happened on the client side and make the clients very, very upset. And that is why we're talking about finalized time state machine. That's our topic today. Yeah. Or we can call it deterministic final automata. Cool. So what does that mean by definition of final state machine? Oh, well, according to Wiki, we have like this. A deterministic final automaton is present formally by five to blah, blah, blah, blah, blah. Honestly, just like the name, don't bother. The definition is definitely, absolutely not understandable. Well, at least for me, I'm not sure, but maybe someone will be smarter than me to understand it. It's not easy to understand. That's one thing. Even if you study computer science, which I did, it's still not understandable. So forget about it. Let's make it simple. In short, deterministic finite automaton is actually represented by a sequence where you have one initial state and a number of finite number of state, which means you don't have infinite number. You have a finite number of state. And then because you have finite number of state, you have a finite number of events that connect these states together and create from here a transition map that triggered by the event to move from one state to another. And so an example of finite state machine that I can think of, and you probably already know, that is the fetch event, fetch sequence, which we have one idle state. When we click on fetch, it will move to, for idle to loading. And then when it finished loading with the result, it will move to success or it will check it will move to failure and depend on the configuration. It can go back to retry, go back to the loading state and retry again. And this is what we call a state machine. So you may wonder by now, that's a state machine. So what's the relationship with state machine and UI? It's talking about fetch. It's not about UI, right? A UI development is about UI. It's nothing related to fetch some data in the background. Am I right? Well, to be honest, let's take a look. This is the user flow or you can call it a mockup. When we talk about UI design or development, we always talk about wireframe like this. A mockup like this, a transition graph, sorry, a transition map from where the designer will write from which page to which page. If you click on my profile, you go to gallery. If you click on a single item in the gallery, you go to my post. If you click on edit profile, you go to a page called edit profile and so on. Well, let's summarize this UI into several insights such as, first of all, as you can see, the UI faces of components are totally not independent. It's all reliant on each other and interact with each other in a very close way. They transition from one component or one page to another and done based on an action, a user action, which in fact, is actually event trigger. Like on click is on click event. On change is on change event and so on. And that's why when we talk about building user interface, we're talking about building user flow or we're talking about building user diagram or user trees. And what is trees? It's actually building graphs. And what is graphs? It's a machine. Same machines is used everywhere. Why so? First of all, you can see these are the same machines. Promise, as in our way, callback, observable, all of them. But not all of them are designed for handling user interface, right? So that's why we have XState. Well, our main topic. Yay, finally. So XState is an open source javascript and typescript support library package for handling state machine and state charts for the user interface, to be short, state machine and state chart for modern web. And for vue, Svel, and react. A lot. Well, to be short, it's just kind of state management for UI, for UI component. So they have a very good website, very detailed with all the tutorial recipe and every detail api step by step. So what do they bring us? The library provides a set of api to create a machine, a state machine, and then get, interpret or get service, a machine service instance from the state machine we created. And then execute the instance according to our configuration and then allow user to manage a whole system of state machines, whether it's nested state machines or it's a parallel state machine. It doesn't really matter. Some other main api we're talking about is machines, transitions, event, action, and state. State are the points where your application, your UI is standing. Machine is a set of states that can choose transition or can communicate with each other, can move from one to another due to some transition map. OK? And the transition map is created, the transition point where the next state is, and the next state is triggered by an event. And an event, it actually causes the state machine to move and also it includes an action that is one-time actions to trigger when the event is fired. OK, so our previous example of the fetch will be something like, in X state, will be something like this. Now we will define a machine with one idle state, which is initial, and then we define the state, we define the idle state, which we have an event called fetch. And on fetch, we will move to loading state. Loading state, we have two events, result or reject. On result, we go to success, and this is the final. On failure, we go to loading on the event retry, and then we can add some context to calculate and to manage the number of retry if we want. So far so good? OK, so we technically already use our own machine to handle the fetch with four states. Now, the next question, how are we going to do this machine, like create a state machine in vue? Well, we can use the normal X state package or we can use the X state slab vue. This is a package dedicated by vue. We provide additional hooks similar to react hooks, with aligned to composition APIs. So it will allow you to start in writing applications and much more straightforward. But don't worry if you're using vue 2, it's still working with vue 2, which is a great thing. So let's take a look at the example, a very simple example we have. Toggle component, which only have two states, inactive or active. So initially, it will have inactive state, and then on toggle event, we will move to active state. And on active state, on toggle event, we'll move back to inactive. Simple, right? To write it in X state, we just need to import the state machine from the X state, write a machine, give it an initial state, inactive, define the two states, inactive with toggle, go to active, and so on, and bypass. So that's it. We have the state. But yeah, you are pretty bored right now because we just look at that code. There's nothing fun there. Seeing code is not fun. So let's do a recording, shall we? Okay, I hope I won't mess it up. Okay, here we go. So just wrap. So I have an image component, I have a toggle button. Sorry, just don't worry about the image. So we have a toggle button. And in the toggle button, we just have a span, we take the state to have the state. And we just change the background and the indicator. And how does it look? Let's say we will have, this is how it looks. It's very simple. It's like go up, go down, and that's it. So we're using the composition api, which means we have a state here, which we're using the ref to put with the value of fill space in the beginning. And then we will have a toggle button, which what it does, it just changes the value and updates the value, and that's it. And of course, we have the compute value to change the css style. How we're going to change it? Okay, let's go. So we don't need this one. Oh. Wait. Wait. So let's just move this. Okay, so now we don't need. Okay. So we don't need this one anymore. So all we need to do is create a new state. Oh, first of all, I'm sorry. First of all, we need to define the toggle, which we're going to go define the toggle in the machine with a toggle code, a toggle machine with the code that we're just talking about. But a little bit of change because here I want to define specific the target and then I will define the actions in addition when the event is triggered, which means on actually when the event is triggered, I want to change the background to the different background and I want to change the indicator to with the new css style. And so on. Same thing with active. And how in order to use this, I'll just go here and start writing. I want to use I want to do I want to create a service from that, which means I need to interpret the machine to get a service. So toggle machine. So on. And now I want to I need to assign the toggle, which means I need to make sure that when I create it, I do toggle the same. You see toggle. When I click on toggle, I want it to send the event toggle. OK. But that's not on. That's not finished yet. We need to make sure that the machine is updated because. Sorry, not like this. Yeah. And so after I get the service, I need to set the initial state, the local state of the component, which I'm using this and I'm using the rep and I put the service initial state. Good, right. But in order for me to make sure that I'm not using the service, for me to this is just creating the first initial value of the local state with initial state. When I want to update the local state here, I need to make sure that that the state update on transition, which means we need on transition. And on transition, receive a new state. And then we can say we can put the state value, update the state value to new state. Awesome. And you can also console lock the new state just to see how it works. And that's it. And here I don't need the background state and I don't need this anymore because I already have the context. So now I can just do state and then context and then background. OK, similarly, I do state context, context indicator and I should be working. Let's just make sure that it's working. Oh, now, token machine sending out a function. OK, so let's see, token machine. Oh, no, I cannot just write token machine. I need to use the service, not the token machine. So I'm doing the service and I'm just restart. Now let's do it. OK, it doesn't work. So let's say I probably forgot, I think it should be state. I probably know. So let's just go to X state and go to X state and I want to see how I'm going to update it. So let's go to the update and go to tutorial. I know last word and let's see state. And transitions type, type, type. On transition, I guess. So it's on transition and then we can do. OK, I have no idea, but let me choose to do recipe or tutorial. Yeah. So let's say if I have this and current is, yeah, so state. So it should be already OK. So here, just the value is the state. So let's just go back here and just do it again. And see how it's working. Yeah, it's working. OK. And let's take a look on the toggle to make sure I'm all set. It should be background indicator. So state context background, state contact indicator. No problem. And with state value. OK, let's say first for that I have this one. Let's say you stay on transition, so service. There was a center girl. So I was right. Why didn't it work? State of value, save. OK. So let's just copy this code just for the sake of it. I may have. Yeah. But you get the idea. So it should work just like that. And now I'll just do it again. And you can see at the state here and we have a history. Also, if we change from one to another, then we have the history value of the previous state and also all the other information. Cool. Now how we're going to do it with the, XStateView, so first of all, we're going to have a service, which we can define in the location way. We can reuse it and other component can access it, which we use the use service is the hook from XStateView. That we will write a function called useToggleMachine with return the new service where we turn on this, we get interpret the service from the toggle machine. And we start the service also. So now here, all I need to do is just to remove. So just to make sure that I don't need this one, all I needed to get this from useToggleMachine like this. And the useToggleMachine, it return the service, return two things, state and send. It's a similar concept like update. Update state in react, use state in react where you get one state and you get the function to update the state. So here we don't need this anymore. And we just need to make sure that the const toggle equal and do send. Send and then we'll put toggle. Okay, and that should be it. Okay, I'm sorry. I need to remove it. I know. Now it should work. And then now you can see, now it works. And now let's think how much things do we have to clean up. We clean up one of these and we clean up one of these. Awesome. So now I'll call it just this one. It's awesome, so now I'll call it just this much. And we can also try to print it out from other component. Let's say an app, I want to print it out. I'm using the toggle machine here. And then I have the deep place for the toggle mode. And then I can see toggle active and active. And that's it. Great. Cool. Okay, so far so good, right? It was just an example where we can see how XState work and how easy to set it up. So what's the advantage of the XState? Well, first of all, you have a very clear view, visual clear view of the code flow. And then you can, because everything is layout and graphs and everything is state and event and one component connect to another, two state and two events. So we can actually reuse the code and we can actually make sure that we have enough test coverage. Because whenever we add another state, we just need to test that state and the connection between any other state that connect directly to this state. We don't need to test anything else. So we can make sure that our test is very precise. We don't break anything. And we can also easily to spot bugs and debug. And of course, scalability and maintenance is awesome. Because we're easy to see, easy to understand, easy to maintain. And because it's a map, it's the transition map, it's the graphs, you can really see how you can design and plan this design and plan your code ahead. So it's better code design for us. So if you really want to check it out, which I totally recommend you to do so, because I love XState very much. I would suggest, first of all, trying to check out the doc site because they have a very nice visualizer. Like here, as you can see, they even have a visualizer and some example of the definition, the state. And here I copied the state, the toggle state machine that we create before. So let's say toggle machine, machine toggle. OK. Then we have toggle and all this one is from the code we put before. One, we have it. And you can see here, you have an app. You click on the toggle, it will go to loading. You click on load, it will go to active. Oh, this is because I added another file. But if we remove this and we update, we will go to active. OK. Then we have only two states. Inactive, we toggle, we move to active. And we toggle, we move to inactive. And we can also modify the state. You can add more states, you can remove states. And it will lay out for you to know exactly where you do the wrong things. And not only that, you can also see the state here, the action here, the children. And in the event, you can do a toggle. You can do a toggle event, you send it, and then you can view automatically what's going on. It's very interactive. And I super recommend you to try it out. Other than that, we have a view community and app state community. It's very active, very good. And you can also read the documentation or some articles about how to build the entire UI component library with app state. Very nice to see. Or some other documents specifically on view. And totally recommend it. I love it. I really, really love it. It requires a lot of learning curve because you really need to change the habits of how you decide to work with how you decide how your code looks like. Rather than jump in and write code, you need to sit down, plan ahead what state you have, and work it from there. But it's really good. It helps you to write better code. Just before we finish, my name is Maya. And I'm a senior software engineer. I'm a co-maintainer of Storefront UI, a component UI library. I'm also an ambassador for Knox. I'm a Google Developer Expert on web performance. And I'm also a vue JS organizer. I like to write about, talk about front-end development and web development in general. And feel free to follow me or to be in contact with me on GitHub or on Twitter at Maya Chavin and my website, mayachavin.com. And that's it. The final thing is don't start coding. Plan, modeling your state, your component, your features, and then you develop from there. That will save you tons of time in later on refactoring on trying to fix bugs, trying to find the bug. And you can check out my sample code in the GitHub link over here. And thank you again for joining my talk. Hope you enjoyed. Thank you.
33 min
20 Oct, 2021

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic