XState: The Visual Future of State Management (React fwdays)
Watch on YouTube (fwdays)
What is this talk about?
This is a video from the React fwdays'21 online conference, that was held on March 27, 2021. (From the video description.)
“Let's talk about what a finite state machine is, and how we could translate that to a more rigorous visual formalism.”
“We have the idle states, and when the fetch event happens, we transition to the loading state, so you could see we're only in one of these states at a time now when we two things can happen from the loading states.”
From the opening of the talk.
Key takeaways
- Let's talk about what a finite state machine is, and how we could translate that to a more rigorous visual formalism. 3:00
- We see that it could go to editing on the edit event now, this is important because the edit event might have actions or side effects, or things that you might want to do. 11:00
- The first thing we provide is the initial states, which is reading, and then we have our different states here, we have states reading and editing. 15:00
- You see that the cancel event happened, and you also see the state machine changing in real time as these events occur. 22:00
- You could also learn how to model state with React and XState, both of those on Frontend Masters. 24:00
Quoted from the transcript.
Full transcript
From YouTube auto-captions. Punctuation and capitals were restored automatically. Technical terms were normalized; some errors remain. 4,518 words.
0:00 Introduction
Hello everyone, my name is David Khourshid. And, today I want to talk to you about the visual future of XState. And so to do that, first, I want to talk about just what exactly XState is about and what it means for it to be a visual formalism. So I don't have any slides, most of my stuff is going to be almost completely code. So X8 is a library, and I'm going to just show you right over here. Actually, there's a library that allows you to create state machines and statecharts in your web applications. It doesn't matter if it's React, view, Angular, or even no framework at all. And all of this is nothing new, it's nothing invented, it all stems from some very old concepts, actually. One of them is a finite state machine, and the extended version of that finite state machine, which was invented by David Harel. Over in this was published in 1987.
So more than 30 years ago called statecharts, and he called statecharts a visual formalism. And this has been extremely appealing to me, and one of the reasons that I've been working on XH for such a long time. A visual formalism combines two things. It combines a formalism which you could think of as a mathematical way of describing some, just something. And the visual parts. Where, instead of just describing it in terms of mathematical symbols and all that rigor that's involved, you describe it visually? And I think the mix of both of those makes it possible to create visual diagrams that, that's can both be rigorous, can be logically sound, but can also be executable. So, when we take a look at what a finite state machine is. And if you look in Wikipedia, you're going to see that it does have a mathematical model over here, and this mathematical model can be considered. That formalism, where we have five parts, we have, and I'll sort of translate this for you.
We have the input, input, alphabet. And you could sort of think of that as events or things that go into the state machine, so stimulus triggers, stuff like that. We have a finite set of states, which is this S symbol over here, and so that's all of the finite states that. This model can be in now. A rule with finite state machines is you could only be in one state and then at a time. And I'm going to explain that soon. There's also in initial states, because all state machines have to start in one state. There's a transition function that talks about, or that describes if you're in one state, in an event, happens now, you're in another state. And there's also a final set, or a set of final states, which describe when the machine stops. Now, typically in web applications, the machine doesn't really stop. You want your users to be able to do so many different things with the application, so this doesn't usually apply, but it is useful in certain circumstances.
So now let's talk about what a finite state machine is, and how we could translate that to a more rigorous visual formalism. Taking all those, we have all the parts that we just described, or most of them actually, we have finite states. Such as you could think of this date machine as describing a promise or fetching data async. We have the idle states, and when the fetch event happens, we transition to the loading state, so you could see we're only in one of these states at a time now when we two things can happen from the loading states. We could have a reject event which goes to an error, or we could have a resolve event which goes to success. Now, some of you might be looking at this and thinking, why do we need this visual formalism? Why do we need finite state machines? Why can't we just write everything using async? Awaits. I like our booleans, our if statements, just the way that we're normally programming. Well, for a trivially trivial example like this, of course, it makes sense that you know you would want to just do what you've been used to doing.
But when it comes to adding complexity and extending it with features? And also being able to describe to your teammates, your other developers, designers, and anyone interested in the logic of your app, what's actually going on? Having a visual model like this is actually very, very helpful. So, for example, here, let's say that we know that our API is flaky, and so when we're in the loading. States we go and the API rejects it for some reason, it goes to the error state. But we want to give the user a chance to retry, so we could do that by adding another arrow here. Maybe that's too many arrows, and if we double click here, we could have a retry event. And now we could visually see how that logic works, we could see that when we're in the error state, we could keep retrying. And also, we know that a reject might not be the only reason that this could fail.
But we could also have just draw an arrow here we, we could also have a timeouts event. And so now, when you're describing this to developers, they can immediately see there's many ways that this could fail either the promise is rejected or a timeout happens. And they could also see as a feature that when we're in the air state, we could retry and go back to the loading states. So that's one feature of state machines and statecharts. That's really useful is just being able to clearly define this logic. And again, this is nothing new. A lot of you, or a few of you, have probably seen diagrams like this before. What's great about this is this is not, specific to any language, it's not specific to any tool. It's something that you could take and apply to absolutely any framework language. Or anything really. So, that's that's what David Harel, this computer scientist, means by visual formalism, where we could take something like this and translate it directly to code.
So I'm going to show you something that I do actually wants to translate directly to code from a visual formalism. And it's this component over here, and I've actually been using this in real life apps.
7:00 If we're reading, the only thing that could happen here is…
And I. I didn't exactly want to pull something off the shelf and try to try to add props and, you know, just customize it instead. I sort of want to just build it myself. So what this is it's just normal text. This is a div input or just a Div, and when you double click it, you're able to edit it. And then to submit this, you could do a few things, you could press enter, you could focus out in the changes. And also if you want to cancel, like, let's say you made a mistake. I could press the escape key, so right now I'm pressing the escape key. And it's going to go back to, to that previous value, which has changed. And so the way that I'm doing this in React is I'm using the useReducer hook, so we're not even using XD just yet. Now. If you've been using use dates to do this, I highly recommend you move to reducer, just because everything becomes a lot clearer.
Using a reducer allows you to consolidate your logic in a single place. So over here, I have a reducer that's handling three events. And so we clearly see that only three things can happen that change the states in this state. I have a status and I have a value, so actually we don't even need that, but I'll keep it there just to be safe. And so the status says. If we're reading, which is this? or double click, if we're editing, which is this? And I could focus out and it will go back to reading. And so we have a clear delineation of what our states are, so we have reading and editing, and what could happen that changes those states. So we could have edits events, we could have a cancel events, and we could have a commit event. Now these are all attached to the UI through here. So, if it's reading, we are just showing that DIV, and when we double click, we're sending.
The React documentation calls this dispatch, but I like, send better. We're sending that type edit event, signaling that we do wants to edit, edit that input. Now when we're in the editing states this input. We have a couple of event handlers here. When we blur, we consider that to commit. Also if we have a key down and the enter key is pressed, that also counts as commit. So, this is really good for deduplicating logic, because we could send the same event. And all the logic is handled up here, instead of it being handled all in the event. Handlers. Like the only. The only conditional logic we have here is to distinguish between the keys, whether we're pressing the escape key or the enter key, and so. Yeah, we have that deduplication, we're able to dry up our code. However, this, especially if you're used to Redux, this would be the most straightforward way of handling events.
But it isn't entirely correct if you were to show a developer this or even try to, describe it to a designer. They would think that these events and these state changes can happen in any state, and that's not exactly correct. I talk about this more in an article called no. Disabling a button is not AppLogic, and I highly recommend you read it because it goes through all the steps. Where, first we're using use dates and then eventually we useReducer. And we see that we actually have a lot of bugs and so refactoring to the events first actually fixes a lot of these bugs and prevents impossible states from happening. So, let's go back to our excalidra and let's see how we could describe this logic in again, a visual formalism. So over here, I have a another state machine diagram, and our initial state, that's what this symbol means. It means, you know, we're going to reading as the initial state.
We see that it could go to editing on the edit event now, this is important because the edit event might have actions or side effects, or things that you might want to do. However, when we're in the editing state and the edit events happens again, we don't want to handle it at all. Because we're already in the editing state, why do we need to handle that edited event again? And then we can see clearly here that two things can happen only from the editing states. We could commit the value, and so we have an action here, a do action, or a transition action that assigns that value. Or we could cancel and we could just go back to the reading states. So let's first design this date machine using our useReducer, so we're not even using any library yet. The code might get a little bit more verbose, but it's going to be a lot more robust. So the first step is instead of switching on the events dot type, we are going to switch on states dot status.
And so this data represents our finite states, either reading or editing, so we could. Say, case reading and I'm just going to return state for now and case editing, and we're going to return save for now. And defaults. Which this should never happen. Because, you know, we're, we're specifying all of our, all of our finite states. Now we're going to nest, these switch statements, and I know some of you are a little bit unsure about this because there's a lot of nesting going on here. And I'm going to copy and paste those, in there. So now we're switching on our state.status first, which this is our finite state, and then we're switching on the events.type. And so this, this. We could now whittle down the events and see which ones are actually possible in each state. So if we're reading, the only thing that could happen here is the edit events for editing. The only things that can happen are the cancel and the commit events.
And so this makes it absolutely impossible that these events can affect the states. If they're, you know, not. For example, if the cancel event happens in reading, no matter what you do to your UI, you know, it might still accidentally happen, it will not be handled over here, so it gives you a bit of safety over there. And we could see that our logic still works pretty nicely over here. I just press the escape key and it went back there. However, you know, I, I agree, this is very verbose. And so let's take this to the next level. Let's actually, useMachine from it, React, and we're also going to create a machine so imports, creates machine from XState. And so we're going to translate this into a statement. G we're going to, we're going to connect this to a steam machine over here. So I'm going to say, const editable machine and use a great machine.
And then this provides an interface for you to, to create state machines directly instead of doing these nested switch statements. So the first thing we provide is the initial states, which is reading, and then we have our different states here, we have states reading and editing. Now already, this is a good start. And there is an existing visualizer for XState, so if you go to the docs and you click. Visualizer and this will actually be updated in the future. And you copy and paste that code over there, click update. We could see that we have our two states. We have reading and editing, and so we know that if we edit, that goes to editing. And so if we add that transition, we'll see that. You know, that transition can be visualized over there, and we have two events from editing, we have cancel and cancel should go back to reading and commits, we'll just say that goes back to reading as well.
So now we can see and we simulate the logic of our app just by clicking through each of those states. So I like how this machine looks so far. I'm going to copy and paste it and bring it back to here. And now we have to do one more thing, which is add extended states, which in XState is called context. So I'm going to say context value and initial value of hello React FW days, so I'm going to put that there. And this is actually shorthand for this. So commits target reading. It's the same thing. And if you don't have any actions, and by actions, I mean this over here, uh. If nothing is happening on the transition, then you could just put the next states as a string. But in this case, I do want to have an action.
17:00 Instead of this reducer just going to go ahead and delete…
So I'm going to add actions and we're going to assign, the value. And this takes, two arguments. We have the current context and the events that just happened, and so we're going to, let's take a look down here. We see that in our reducer, we are reading from events.value, so we're going to do the same thing over here, we're going to return event.value. And so that's going to assign that value directly to the value of the machine context. Now, instead of this reducer just going to go ahead and delete that. We are going to use the useMachine hook. And I'm going to show you how similar it is to the used reducer hook instead of editable reducer. We're using our machine, so I'm going to put machine here now. We don't need any of this because the initial state is already encoded inside the machine now. Let's console.log just to see what comes out here, console.log state and this might be small, but you could see, oh yeah, that is pretty small.
There's a bunch of things that you get, you get a value, which is the value of the finite state, and you also get contacts, which is hello, React FW days. Other things that you get are the event, which is the latest event, in this case, it's just an event. And you'll see that change as we go through the machine and a few other cool things which aren't really necessary here. Those are the three important things, though. So let's zoom back out. And now, instead of state.status, this is going to be state dot value, like we saw here and so we see. Oh so, this is going to be states.context.value, and over here is also going to be state.context.value.
19:00 You could also pop this out of an iframe…
So now we have hello React FW days and we can see that when we double click and change this, press enter, it's changed. Never mind, I'm going to press escape, and it's going to go back to change this. Our logic works exactly the same. All right, so now this brings me to my main point, which is why do we want to do this? Why should we use like state machines and have this in this formalized? Basically a big JSON object, and the reason is because we could translate this directly to this. So if I were to copy and paste all of this and go back to the visualizer update, we could see that. You know, our logic is visualized from whatever we programmed. From the state machine, and this is extremely useful. But I want to show you something else. If I go back to the index, we have that state machine description.
And we're also, you know, we have a very robust, formalized way of listening to events and state changes. And so it's possible to actually inspect this machine in real time, and so I'm going to show you how to do that. There's a package called X8 inspect, and from it you could get the inspect function, and all you have to do is call Inspects before your app starts. But once you do this, edit the HTML, this is going to show up in an iframe. And it might be too small. And I'm going to show you how to pop that out into a window. So if we reload this, you're going to see that in XState. Window pops up over here and it says waiting for connection, and it says that. Because what we need to do is we need to set over here and actually collapse this. If I can. We're going to set dev tools, it's true, and so this is going to allow this machine to be connected to the Dev tools.
So once we reload now, you could see that we have the, the visualization of the machine as it's happening in real time. So as the states are changing, as events are occurring. So if I double click this, you see that it goes to editing and so I could change this. Press enter and you see that it goes back to reading. And you could see the context, all the state values over here, and we also have a sequence diagram right here that shows exactly what happened, so you could see. I double clicked it, said edit, and it's doing double. That's probably because of fast refresh or something, so it says no events yet, double click. So I have commit over here, I'm also committing if I this and I press escape. You see that the cancel event happened, and you also see the state machine changing in real time as these events occur. Now. You could also pop this out of an iframe, so if we do inspect and say iframe false, that's actually going to pop up a new window.
And now you could see this machine changing in real time. And what's cool about this is, I'm going to put the iframe back in just to show you, is that it goes both ways. This iframe is being controlled by the machine here, but it could also control the machine in the app.
23:00 The final states in React component by using XState
So if I press edit here and I press cancel here, you see that the logic goes, you know, both ways over there. And so that is. What is so powerful about a visual formalism is that the logic in your app can be visualized. It's just you have to apply some rules, which is, you know, known as finite state machines to it. And uh. And exit allows you to create those finite C machines and have them visualized in real time. Now, this is, this is part of the future of XDA, like right now. Like, this is actually, you know, something that you could use today if you're using XState and see your see your state changes in real time. In the future, it's going to be possible to edit this date machine using drag and drop tools and be able to visualize it and have it shared with your app. Or have it be in a sort of registry where you could share machines with other people.
And it's going to look pretty similar to this. So, yeah, that's what's meant by a visual formalism. So I. I highly recommend that you check out XState and also join, we have a community at Discord and, and just read through the docs, because, XState. It's something again that it's. It's been around for a while, and it's based on ideas that have been around for half a century really. There's a lot of documentation and a lot of resources to get you started, no matter which framework you're using. I also have a, a course on front-end Masters where you could learn the basics of state machines in JavaScript. With XState. And you could also learn how to model state with React and XState, both of those on Frontend Masters. There's also a couple of articles that I talked about, one of them is disabling a button is not app logic, which talks about going from use dates to useReducer. To a state machine, and then finally to XState, and also if you don't want to use XState.
I wrote an article titled you don't need a library for state machines, which sort of gives you options to understand how you could create state machines without needing to pull in the library. Now, x8 is most useful for statecharts, which are an extended form of state machines, which include hierarchical or nested states, orthogonal or parallel states. History, entry, exit actions, actions on transitions and a lot more. So, I highly recommend you check this article out because using finite steam machines should be something that should be in your development and planning. Process whether or not you use the library. So, with that said, thank you, React. FW days. Hello David, thank you for your talk. It was really curious and interesting. And my question is, is it possible to use it in other frameworks? Yes, XC can be used in other frameworks. In fact, there's adapters for Vue, for spelt, and, curiously enough, XState just happens to work with Angular. Because the services that XState generates from the machines, or when the machine is interpreted, it's called a service and that is already subscribable.
So if you're using RxJS or anything that like anything that uses observables, then, it could be used with that. But yeah, you could use it with any framework, or no framework at all. So it's like a silver gun which can be used everywhere. Yeah, yeah, I, I do have to say that, state machines are a very powerful tool, but it's not something that should try to be used everywhere. In fact, I always encourage people to try modeling their logic the way that they're used to modeling it. And once it gets complex enough, use steep machines as a refactor target so they could refactor their app using XState. And that's when it becomes most useful, when you know all of the requirements of your logic. Okay? And what about how to listen for final states in React component? Can you repeat that? Yeah, sure. Uh. How can I listen? The final states in React component by using XState. Oh so, can you hear me? Yes, I could hear you.
Listening to final states, can be done by. If you have access to the service. There's an on done callback handler, and so when the machine reaches a final state, that on done handler will be called. Okay, sorry, because I have like a misunderstanding with drops and I didn't think that you have finished the answer. My next question is what happens if you dispatch on an event that doesn't exist in React XState? If you dispatch an event that doesn't exist, then it's going to be ignored by the machine. And this is a property of state machines, where any unhandled events just are completely ignored. Now in XState, there's something called the wildcard events, which, if no other events, is.
29:00 Services that are invoked are actors, promises are technically actors…
Matched it will, it will match that event. Listen to any events that way, but for the most part, it's ignored. Okay, and my question is, I have started your course in the Frontend Masters and I have got, like, a misunderstanding. What is the difference between an actor and involving services in XState? It was will, it will be my last question. Okay, that's a good question. There is no difference. So in actor, which this is a whole other talk. In itself. Is something that could receive events, and, it has its own internal states, and it could send events to other actors. So, services that are invoked are actors, promises are technically actors, observables are actors as well. And in version five, all of this is going to be unified, so there's going to be no difference between an invoked service from a machine or another actor. All of them are going to adhere to the same send and receive event interface.
Okay, so, thank you so much for joining us. See, maybe it will be a luck to see you next year in an offline version in our conference. Thank you so much for joining, joining us. Okay, I hope so, too. Thank you for having me.
Related talks
-
XState: the Visual Future of State Management
React Summit Remote Edition 2021. 27:00, 2.1K views -
Modeling Application Logic Visually
Certified Fresh Events, 2022. 42:23 -
Modeling Application Logic Visually (Extended Session)
Certified Fresh Events, 2022. 47:13 -
Introduction to statecharts
React Finland Mini-Conference #2 (2020). 44:07, 1.3K views