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.
Vuex? No, it’s X(state)Vue for UI
AI Generated Video Summary
1. Introduction to State Management in Vue
Hi everyone! In this part, I will talk about state management for UI in Vue, specifically focusing on X state for Vue and UI. Vue 3, released last year, has brought significant improvements, including the Composition API, which allows for more flexibility and control in component development. With Vue 3 and composition, we can easily create dynamic components, such as rendering an image on button click. However, we need to handle scenarios like disabling the button during image loading, showing a loading state, and handling image load errors.
Hi everyone! It's very nice to be here, back to Vue.js London for the second time. 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.
Okay, 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's the main thing about Vue 2? Vue 2 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 in how we want to view the component rather than the default option template that very limited in allowing us to write a component in Vue.
Now we can write more and, sorry, now we can write more flexible and more dynamic components, such as functional component easier. And, for example, let's say 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. So, first of all, we have a setup method in the template, and now we can set a local state by using REF. So, we can get a kitty with the REF with the default value is empty, and then we can assign a function to trigger when the user clicks on a button to update this local state to the image that we want once we fetch the image successfully. And then, we return a render function, which will be written in JSX if you want to. So, it's very similar to React in a way, so React developer will be more easier to adapt to the new view, and the render components will render 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 key thing. That's it. Okay. Nothing very complex here.
But let's say if the product managers 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. The user can click it multiple times. What are we going to do? We need to handle it by disabling 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.
2. Challenges with the Bottom-Up Approach
At some point, we end up with a component that becomes difficult to understand, maintain, and test. The bottom-up approach, although common and initially convenient, leads to complexity and challenges over time. As we add more functionality and bug fixes, the component grows and becomes harder to comprehend. This can result in difficulties in testing and understanding the use cases. Ultimately, this can lead to frustration for clients on the receiving end.
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, a lot 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, inloading, 20%. Inloading, 40%, display a different animation effect, transition effect, display a different error state when error message, when the user, let's say, the server return 500, 4, something for the user, more and more use cases, adding more and more problems 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. Okay, 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 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 core component that become growing and growing with more functionality adding, more functionality adding, different more core adding, it's become hard to understand, hard to maintain. And then because it's hard to maintain, it's hard to test how we go to make sure that what we test, what we wrote, we'll not break something somewhere that we don't know, we don't understand. They 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 on the use case is for handling. And because of that, if something happens, we will not be able to understand whether we cover the test or whether the step happen. That's hard to understand. And at some point we will get to this one. And this is not on our side. It's actually happened on the client side and make the clients very, very upset.
3. Introduction to Finalized State Machine
Today, we're discussing the finalized state machine, also known as deterministic finite automata. The formal definition of a deterministic finite automaton can be complex and difficult to understand. Even those with a background in computer science may struggle to grasp it. So, let's simplify things.
And that is why we're talking about finalized state machine. That's our topic today. Or we can call it deterministic finite automata. So what does that mean by definition of finite state machine? Oh, well, according to Wiki, we have like this. A deterministic finite automaton is formally by five to blah, blah, blah, blah. Honestly, just like the name, don't bother. The definition is definitely, absolutely not understandable. Well, and this is for me. I'm not sure about everyone. Maybe someone that will be smarter than me that understand it. It's not easy to understand. That's one thing. Even if you have studied computer science, which I did, is still not understandable. So forget about it.
4. Introduction to State Machines in UI
Deterministic finite automaton is represented by a sequence of states connected by events in a transition map. An example is the fetch event, where states transition from idle to loading, success, or failure. The relationship between state machines and UI may seem unrelated, but in UI development, we design transitions between components based on user actions, creating a user flow. This interdependence of UI components forms a graph, which is a state machine.
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 mean you don't have infinite number. You have a finite number of states. 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 trigger by the event to move from one state to another.
And so an example of final 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 it is to loading. And then when it finishes loading with the result it will move to success or it will check it will move to failure and depend on the on the configuration it can go back to retry, go back to the loading state and retry again. And this is what we call 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? 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 one frame like this. A mockup like this, a transition, a transition, sorry, a transition map from 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 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 interacts with each other in a very close way. They transition from one component or one face to another and done based on an action, a user action. Which, in fact, is actually event trigger. Like on click, it's on click event. And 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 are trees? It's actually building graphs. And what are graphs? A state machine.
5. Introduction to Xdate Library
The Xdate library offers a comprehensive website with detailed tutorials, recipes, and API documentation. It allows users to create machines, define states, transitions, events, actions, and manage the entire system of state machines. The machine represents a set of states that can transition or communicate with each other based on a transition map. States are the points where the UI stands, and events trigger state transitions. Actions can be defined to execute when events are fired. Xdate simplifies the process of creating and managing state machines for UI components.
In our previous example of the fetch event, we defined a machine with an initial idle state. The fetch event transitions the machine to the loading state. The loading state has two possible events: result and reject. On the result event, the machine transitions to the success state, which is the final state.
6. Introduction to State Machines in Vue
States are the points where your application, your UI is standing. Machine is the set of states that can choose transition or communicate with each other, can move from one to another due to some transition map. And the transition map, it created the transition defined where the next state is, and the next state is triggered by an event. And, of course, an event, it actually caused the state machine to move. We can technically build our own machines to handle the fetch with four states. To create a state machine in Vue, we can use the XJ package or the XJ Slack Vue package, which provide additional hooks and allow for composition APIs. Let's take a look at a simple example of a toggle component with two states, inactive and active. To write it in X state, we just need to import the state machine from X state, define the initial state, inactive, and the two states, inactive and active.
States are the points where your application, your UI is standing. Machine is the set of states that can choose transition or communicate with each other, can move from one to another due to some transition map. Okay. And the transition map, it created the transition defined where the next state is, and the next state is triggered by an event. And, of course, an event, it actually caused the state machine to move. And also, it's an include an action that is a one-time action to trigger when the event is fired.
Okay. So, our previous example of the fetch will be something like in the next state, will be something like this. Now we will define the machine. One is 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 retry. Sorry, we go to loading on the event retry and then we can add some context to calculate, to manage the number of retries if we want. So far so good.
Okay, so we can hope we technically already build our own machines to handle the fetch with four states. Now, the next question, how do we go to do this machine, like create a state machine in Vue. Well, we can use the normal XJ package, or we can use the XJ Slack Vue, this is a package dedicated by Vue, with provide additional hoops, similar to React hoops, and with allowing to composition APIs. So it will allow you to start and 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 an example, a very simple example. We have toggle component, which only has two states, inactive or active. So initially, it would have inactive state, and then on toggle event, we will move to active state, and our active state on top of it, 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 bypass. And that's it. We have the state. But yeah, you are pretty bored by now, because we don't have, we just look at that code. There's nothing fun there.
7. Real Coding with Toggle Button
Seeing code is not fun. So let's do a real coding, shall we? We have an image component and a toggle button. The toggle button changes the background and indicator. We're using the composition API, with a state and a toggle button that updates the value. We create a new state, define the toggle in the machine with specific targets and actions, and create a service from it. Finally, we assign the toggle to the machine.
Seeing code is not fun. So let's do a real coding, shall we? OK. I hope I won't mess it up. OK. Here we go.
So just wrap. So I have an image component. I have a toggle button. Sorry, don't, just don't worry about the image. So we have a toggle button. And in the toggle button, we just have a span which takes the state to have this 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 can go up, go down, and that's it.
So we're using the composition API, which means we have a state here which we are using the ref to put with the value of fail space in the beginning. And then we will have a toggle button, which what it does, it just changes a value and update 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, so let's just move this. Okay, so now we don't need. Don't. We don't need this one anymore, so all we need to do, we 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 the toggle code and the 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 indicator to with the new CSS styles, styling and so on, same thing with acting. And how in order to use this, I'll just go here and start writing, 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 need to assign the toggle, which means I need to make sure that when I create it, I do toggle the send machine. Toggle.
8. Updating the Local State and Testing Functionality
When I click on toggle, I want it to send the event toggle. We need to make sure that the machine is updated. After getting the service, I need to set the initial state of the component. When updating the local state, ensure that the state is updated on transition. Console log the new state to see how it works. Remove unnecessary code and use the context to access the state. Test the functionality to ensure it's working correctly.
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, 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 ref and I put the service initial state. Good, right? But in order 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. Similarly, I do state, context, context indicator, and I should be working. Let's just make sure that it's working. Oh, now. ToggleMachine send another function. OK, so let's see ToggleMachine. Oh, no, I cannot just write ToggleMachine. I need to use the service, not the ToggleMachine. 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? No. So let's just go to X day and go to X day. And I want to see how I'm going to update it. So let's go to the update and go to Tutorial. No last word. And state. And transitions type, type, type.
9. Transition and Toggle Functionality
On transition, I guess. Let me choose to do recipe or tutorial. Let's take a look on the toggle to make sure I'm all set. It should work just like that. We have a history, including the previous state and other information.
On transition, I guess. So it's on transition, and then we can do. Okay, I have no idea. But let me choose to do recipe or tutorial. Yeah, so let's say if I have this and recent and current is, yeah, so stay, so should be already okay. So he just stop value is the state. So let's just roll back here and just do it again. And see how it's working. Yeah, it's working.
Okay. And let's take a look on the toggle to make sure I'm all set, should be background indicator. So context background, state contact indicator. No problem. And with state value. Okay, let's say first for that, I have this one. Let's say new state on transition. So service. There was a send toggle. So I knew I was right. Why didn't it work? State of value, save. Okay. 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. You can see at the state here, 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.
10. Implementing XDAView and Printing Toggle State
Now, let's see how to implement it with XDAView. We define a service using the use service hook from XDAView, which can be accessed by other components. The useToggleMachine function returns a service that interprets the ToggleMachine service and starts it. We remove unnecessary code and use the toggle from useToggleMachine. The toggle function updates the state. We clean up the code and print the toggle state in an app component.
Now, how I'm going to do it with the XDAView. 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 will use the use service. It's the hook from XDAView. That will write a function called useToggleMachine which returns the new service, where we can interpret the service from the ToggleMachine and will start the service also.
So now here, all I need to do is just to remove, just to make sure that I don't need this one. All I need is to get this from useToggleMachine like this. And the useToggleMachine, it returns the service, returns two things, state and send. It's a similar concept like update state in React, useState 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. 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 worked. 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 code it just this much. And we can also trying to bring it out from all the components, let's say an app I want to print it out. I'm using the toggle machine here. And then I have to de-place for the toggle mode and then I can see toggle active and active. And that's it, great, cool.
11. Advantages of Xstate and Community
Xstate provides a clear view of code flow, enables code reuse, precise testing, bug spotting, and easy maintenance. The transition map allows for better code design and planning. Check out the XDAY dark side visualizer for a great experience. Modify and view states, actions, and events interactively. Join the active AppState community and explore documentation on building UI component libraries.
Okay, so far so good, right? It was just an example where we can see how Xstate work and how you need 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 through state and to 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 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 the 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 XDAY very much. I would suggest, first of all, trying to check out the dark side 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. Toggle. Then we have toggle on this one, it's from the code we put before. One, we have it and you can see here, you have an active, you click on the toggle, we want to load it. You click on load, we want to active. Oh, this is because I add in another file, but if we remove this and we update, we will go to active. Active, okay. Then we have only two state. Inactive, we toggle, we move to active. When we toggle off, we move to inactive. And we can also modify the state, you can add more state, you can remove state, 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 in the children. And then the event, you can do a toggle, you can do a toggle event, you send it, 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, an AppState community, very active, very good. And you can also read the documentation or some articles about how to build the entire UI component library with AppState, very nice to see.
12. Conclusion and Contact Information
Or some other documents specifically on Vue. It requires changing your coding habits and planning ahead to write better code. I'm Maya, a senior software engineer, co-maintainer of Storefront UI, ambassador for Knox, and a Vue.js organizer. Don't start coding right away. Plan your state, component, and features to save time later. Check out my sample code on GitHub. Thank you for joining my talk!
Or some other documents specifically on view. And totally recommended, I love it. I really, really, just 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 say you have and working from there, but it's really good. It's help 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, which 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 developer and web development in general. And feel free to follow me or to be in contact with me on GitHub or on Twitter at mayachavin 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 develop from there. That will save you tons of time in later on if we factor it on trying to fix bug, trying to find a 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 enjoy it.