Developer Insights
Join millions of viewers! Our engineers craft human-written articles solving real-world problems weekly. Enjoy fresh technical content and numerous interviews featuring modern web advancements with industry leaders and open-source authors.
Introducing the Vue 3 and XState kit for starter.dev
*Starter.dev is an open source community resource developed by This Dot Labs that provides code starter kits in a variety of web technologies, including React, Angular, Vue, etc. with the hope of enabling developers to bootstrap their projects quickly without having to spend time configuring tooling.* Intro Today, we’re delighted to announce a new starter kit featuring Vue and XState! In this blog post, we’ll dive into what’s included with the kit, how to get started using it, and what makes this kit unique. What’s included All of our kits strive to provide you with popular and reliable frameworks and libraries, along with recommended tooling all configured for you, and designed to help you spin your projects up faster. This kit includes: - Vue as the core JS framework - XState for managing our application’s state - CSS for styling - Cypress component testing - Vue Router to manage navigation between pages - Storybook for visual prototyping - ESlint and Prettier to lint and format your code How to get started using the kit To get started using this kit, we recommend the starter CLI tool. You can pass in the kit name directly, and the tool will guide you through naming your project, installing your dependencies, and running the app locally. Each kit comes with some sample components, so you can see how the provided tooling works together right away. ` Now let’s dive into some of the unique aspects of this kit. Vue 3 Vue is a very powerful JS framework. We chose to use Vue directly to highlight some of the features that make it such a joy to work with. One of our favorite features is Vue’s single file components (SFC). We can include our JavaScript, HTML, and CSS for each component all in the same file. This makes it easier to keep all related code directly next to each other, making it easier to debug issues, and allowing less file flipping. Since we’re in Vue 3, we’re also able to make use of the new Composition API, which looks and feels a bit more like vanilla JavaScript. You can import files, create functions, and do most anything you could in regular JavaScript within your component’s script tag. Any variable name you create is automatically available within your HTML template. Provide and Inject Another feature we got to use specifically in this starter kit is the new provide and inject functionality. You can read more details about this in the Vue docs, but this feature gives us a way to avoid prop drilling and provide values directly where they’re needed. In this starter kit, we include a “greeting” example, which makes an API call using a provided message, and shows the user a generated greeting. Initially, we provided this message as a prop through the router to the greeting component. This works, but it did require us to do a little more legwork to provide a fallback value, as well as needing our router to be aware of the prop. Using the provide / inject setup, we’re able to provide our message through the root level of the app, making it globally available to any child component. Then, when we need to use it in our ` component, we inject the “key” we expect (our message), and it provides a built-in way for us to provide a default value to use as a fallback. Now our router doesn’t need to do any prop handling! And our component consistently works with the provided value or offers our default if something goes wrong. ` ` Using XState If you haven’t had a chance to look into XState before, we highly recommend checking out their documentation. They have a great intro to state machines and state charts that explains the concepts really well. One of the biggest mindset shifts that happens when you work with state machines is that they really help you think through how your application should work. State machines make you think explicitly through the different modes or “states” your application can get into, and what actions or side effects should happen in those states. By thinking directly through these, it helps you avoid sneaky edge cases and mutations you don’t expect. Difference between Context and State One of the parts that can be a little confusing at first is the difference between “state” and “context” when it comes to state machines. It can be easy to think of state as any values you store in your application that you want to persist between components or paths, and that can be accurate. However, with XState, a “state” is really more the idea of what configurations your app can be in. A common example is a music player. Your player can be in an “off” state, a “playing” state, or a “paused” state. These are all different modes, if you will, that can happen when your music player is interacted with. They can be values in a way, but they’re really more like the versions of your interface that you want to exist. You can transition between states, but when you go back to a specific state, you expect everything to behave the same way each time. States can trigger changes to your data or make API calls, but each time you enter or leave a state, you should be able to see the same actions occur. They give you stability and help prevent hidden edge cases. Values that we normally think of as state, things like strings or numbers or objects, that might change as your application is interacted with. These are the values that are stored in the “context” within XState. Our context values are the pieces of our application that are quantitative and that we expect will change as our application is working. ` Declaring Actions and Services When we create a state machine with XState, it accepts two values- a config object and an options object. The config tells us what the machine does. This is where we define our states and transitions. In the options object, we can provide more information on how the machine does things, including logic for guards, actions, and effects. You can write your actions and effect logic within the state that initiates those calls, which can be great for getting the machine working in the beginning. However, it’s recommended to make those into named functions within the options object, making it easier to debug issues and improving the readability for how our machine works. Cypress Testing The last interesting thing we’d like to talk about is our setup for using component testing in Cypress! To use their component testing feature, they provide you with a mount command, which handles mounting your individual components onto their test runner so you can unit test them in isolation. While this works great out of the box, there’s also a way to customize the mount command if you need to! This is where you’d want to add any configuration your application needs to work properly in a testing setup. Things like routing and state management setups would get added to this function. Since we made use of Vue’s provide and inject functions, we needed to add the provided value to our ` command in order for our greeting test to properly work. With that set up, we can allow it to provide our default empty string for tests that don’t need to worry about injecting a value (or when we specifically want to test our default value), and then we can inject the value we want in the tests that do need a specific value! ` Conclusion We hope you enjoy using this starter kit! We’ve touched a bit on the benefits of using Vue 3, how XState keeps our application working as we expect, and how we can test our components with Cypress. Have a request or a question about a [starter.dev] project? Reach out in the issues to make your requests or ask us your questions. The project is 100% open sourced so feel free to hop in and code with us!...
Jul 17, 2023
6 mins
State Machines using XState and Svelte (Part 1)
In this blog post we'll learn about state machines, and how to implement them in Svelte with XState....
Jan 11, 2022
5 mins
XState with React for Beginners
Introduction When I started to learn XState for the first time, I found the docs somewhat difficult to interpret. Perhaps this was because the docs show a number of concepts at the same time that I was not familiar with as a developer coming from a Redux or React Context background. So I decided to write this article to help others learn XState step-by-step! What is XState? XState is a state management library for JavaScript. The idea behind it is a little bit different than Redux or React Context as it is not necessary to use a global store or wrap the whole application in a provider. It is just a simple way to manage the state of your application, and separate the logic from the view so it helps you to write clean and readable code. Getting started In this tutorial, we will use XState with 3 React examples that will increase in difficulty. Introductory level (Counter example) In this example, we will create a simple counter application with React, and use XState to increment and decrement the counter state. After you create a new React project using create-react-app, clean the starter template, and build this simple UI in the App.js file. Now install XState, and add it using this command: ` Also, we will need @xstate/react to connect XState with React. ` Now, create a new file, name it counterMachine, and add the following code: ` As you can see, we are using the createMachine function from XState to create a new machine, and we are using the context property to define the initial state of the machine. Also, we are using the on property to define the events that will trigger the transitions. Now, let's create the context (the initial state to our state machine). ` Let's add our two events to our state machine. First event is INCREMENT, and it will increment the counter by one. ` Second event is DECREMENT, and it will decrement the counter by one. Then our state machine is ready. Now let's use it in our React component. Import the useMachine hook from XState React. ` And import our state machine itself. ` Then, add this line inside our component: ` So, the useMachine hook will return an array with the current state, and the send function. And we can use the send function to trigger the events. You can use the state in the JSX like this: ` And now, we can trigger the events: ` And here is the result, run npm run dev: As you see, it's very simple. Now, let's take a step forward and create an intermediate level example. Intermediate level (Todo example) Now we understand how to create a state machine and how to use it in React. Let's create a Todo application to learn how to pass a payload to the state machine. We will use a template similar to the previous one. Let's start with our state machine. First, the context of the machine will look like this: ` and the events: ` And this is how we will pass the payload with the send function to the state machine from the React component. ` Here is the example, run npm run dev: Try it and program the delete the todo function yourself! Advanced level (Traffic light example) We learned how to create a simple state machine in the first example, and learned how to pass a payload in the second example. Now, let's create a traffic light example, and learn about the states in XState state machine. The state of the state machine is exactly like the traffic light. When the traffic light is red, the next state should be yellow, then green, then back to red again. So we will create a simple UI like this with three buttons: Clone the repo from GitHub And then our state machine will be like this: ` So, as you can see, we have two new properties in the state machine. initial and states. And the initial property is the initial state of the machine. And the states property is an object that contains all the states of the machine. And we are using only one event which is NEXT and it will trigger the transition to the next state. So let's move on to the React component, and add the useMachine hook. To know what the current state of our state machine is, we will use state.value. ` So as you see, for each color in the traffic light, we will have a button. And we will disable the button if the current state is not the same as the color. And we will add the onClick event to the button. You can test it here, run npm run dev:...
Jan 10, 2022
4 mins
Using XState Actors to Model Async Workflows Safely
In my previous post I discussed the challenges of writing async workflows in React that correctly deal with all possible edge cases. Even for a simple case of a client with two dependencies with no error handling, we ended up with this code: ` This tangle of highly imperative code functions, but it will prove hard to read and change in the future. What we need is a way to express the stateful nature of the various pieces of this workflow and how they interact with each other in a way in which we can easily see if we've missed something or make changes in the future. This is where state machines and the actor model can come in handy. State machines? Actors? These are programming patterns that you may or may not have heard of before. I will explain them in a brief and simplified way but you should know that there is a great deal of theoretical and practical background in this area that we will be leveraging even though we won't go over it explicitly. 1. A state machine is an entity consisting of state and a series of rules to be followed to determine the next state from a combination of its previous state and external events it receives. Even though you might rarely think about them, state machines are everywhere. For example, a Promise is a state machine going from _pending_ to _resolved_ state when it receives a value from the asynchronous computation it is wrapping. 2. The actor model is a computing architecture that models asynchronous workflows as the interplay of self-contained units called actors. These units communicate with each other by sending and receiving events, they encapsulate state and they exist in a hierarchical relationship, where parent actors spawn child actors, thus linking their lifecycles. It's common to combine both patterns so that a single entity is both an actor and a state machine, so that child actors are spawned and messages are sent based on which state the entity is in. I'll be using XState, a Javascript library which allows us to create actors and state machines in an easy declarative style. This won't be a complete introductory tutorial to XState, though. So if you're unfamiliar with the tool and need context for the syntax I'll be using, head to their website to read through the docs. Setting the stage The first step is to break down our workflow into the distinct states it can be in. Not every step in a process is a state. Rather, states represent moments in the process where the workflow is waiting for something to happen, whether that is user input or the completion of some external process. In our case we can break our workflow down coarsely into three states: 1. When the workflow is first created, we can immediately start creating the connection, and fetching the auth token. But, we have to wait until those are finished before creating the client. We'll call this state "preparing". 2. Then, we've started the process of creating the client, but we can't use it until the client creation returns it to us. We'll call this state "creatingClient". 3. Finally, everything is ready, and the client can be used. The machine is waiting only for the exit signal so it can release its resources and destroy itself. We'll call this state "clientReady". This can be represented visually like so (all visualizations produced with Stately): And in code like so ` However, this is a bit overly simplistic. When we're in our "preparing" state there are actually two separate and independent processes happening, and both of them must complete before we can start creating the client. Fortunately, this is easily represented with parallel child state nodes. Think of parallel state nodes like Promise.all: they advance independently but the parent that invoked them gets notified when they all finish. In XState, "finishing" is defined as reaching a state marked "final", like so ` Leaving us with the final shape of our state chart: Casting call So far, we only have a single actor: the root actor implicitly created by declaring our state machine. To unlock the real advantages of using actors we need to model all of our disposable resources as actors. We could write them as full state machines using XState but instead let's take advantage of a short and sweet way of defining actors that interact with non-XState code: functions with callbacks. Here is what our connection actor might look like, creating and disposing of a WebSocket: ` And here is one for the client, which demonstrates the use of promises inside a callback actor. You can spawn promises as actors directly but they provide no mechanism for responding to events, cleaning up after themselves, or sending any events other than "done" and "error", so they are a poor choice in most cases. It's better to invoke your promise-creating function inside a callback actor, and use the Promise methods like .then() to control async responses. ` Actors are spawned with the spawn action creator from XState, but we also need to save the reference to the running actor somewhere, so spawn is usually combined with assign to create an actor, and save it into the parent's context. ` And then it becomes an easy task to trigger these actions when certain states are entered: ` Putting on the performance XState provides hooks that simplify the process of using state machines in React, making this the equivalent to our async hook at the start: ` Of course, combined with the machine definition, the action definitions and the actor code are hardly less code, or even simpler code. The advantage of breaking a workflow down like this include: 1. Each part can be tested independently. You can verify that the machine follows the logic set out without invoking the actors, and you can verify that the actors clean up after themselves without running the whole machine. 2. The parts can be shuffled around, and added to without having to rewrite them. We could easily add an extra step between connecting and creating the client, or introduce error handling and error states. 3. We can read and visualize every state and transition of the workflow to make sure we've accounted for all of them. This is a particular improvement over long async/await chains where every await implicitly creates a new state and two transitions — success and error — and the precise placement of catch blocks can drastically change the shape of the state chart. You won't need to break out these patterns very often in an application. Maybe once or twice, or maybe never. After all, many applications never have to worry about complex workflows and disposable resources. However, having these ideas in your back pocket can get you out of some jams, particularly if you're already using state machines to model UI behaviour — something you should definitely consider doing if you're not already. A complete code example with everything discussed above using Typescript, and with mock actors and services, that actually run in the visualizer, can be found here....
Dec 8, 2021
6 mins
Let's innovate together!
We're ready to be your trusted technical partners in your digital innovation journey.
Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.