Just want to mention that we're very close to releasing XState v5 beta, which brings even more features to the state machines & statecharts you can create, and greatly improves the developer experience, but also makes it more usable as a general-purpose state management library (or orchestration "framework" if you want to consider it that), whether on the frontend or backend.
Hello! I’ve looked at XState a bunch of times but always felt it seemed very involved for what it does. A simple “vanilla” state machine isn’t particularly complicated.
Are you sure you need to bring “even more features”, or rather, what are your thoughts on how feature-rich a state machine library needs to be?
Would you consider supporting Deno? (The npm module can probably be imported as-is in newer Deno versions, but a dedicated module is always nice and likely wouldn't be much work)
If it (probably) already works as-is, why not just use it? Why is it nicer with a "dedicated module" if using it from npm is the same? More work is more work, reducing maintainer work is something we should strive towards, rather than adding "just a little bit of work".
I know this probably isn’t on the radar, but process trees or pétri nets with token visualization feels doable, any interest in supporting those kinds of tools?
Yes! Visualizing how different actors communicate with each other in general is a big goal for us this year. We're going to start with sequence diagrams.
I was one half of a team that heavily leveraged XState in 2020. We used it to build a general-purpose kiosk app (in Electron) that could be updated remotely using an Xstate configuration served by a graphene-django app. One of the most technically impressive pieces of software I've ever had the pleasure to work on. (I mostly did server/backend stuff).
The entire kiosk device could be repurposed on the fly and Xstate + JSON schema form allowed for completely customizable & swappable UI.
I'm also exploring xstate for a similar use case and would love to know more about your experience with it. I'm particularly interested in understanding the level of customizability you allowed in your implementation.
My use case involves implementing various user flows in my app and I'm currently weighing the benefits of using xstate versus react-router.
Also, I'm curious about how you integrated graphql into this project. Could you share some details on that?
Lastly, I wanted to ask if you stored all the different configurations on a database or on git? If you used a database, how did you manage different schema versions?
Direct from the mouth of the other half of the team:
We used Xstate over react-router by using a parallel machine:
- One for application state
- One for view state
We had to hack the innards of the events a bit to get the two machines to communicate. This allowed the view to re-render the jsonschema form at the exact right time, and not flicker on multiple state transitions.
In hindsight, graphql wasn’t as necessary as we initially thought. The important part was storing the mutation as a string as an xstate transition parameter. We used federation, so it did help us deal with multiple backends.
All database. Versions were handled by having a core machine that pulled the kiosk’s workflow from the server and ran it as a submachine. So the code on the device (core machine) could switch sub machine workflows on the fly between runs.
(we pushed to open source the guts, but to no avail)
Both of us reminisce about it though. We literally retooled the fleet of devices in a weekend to adapt to COVID and it worked. We interfaced with tons of hardware and even ran a customized debian image, all powered by RPis. Truly a nerd's dream project.
I'm a simple man. I just want to embed a state machine in my objects. I want to let it know when an event occurs. I want it to let me know when an event occurs. Sigh..
Found xstates desire too control everything made a simple embedding use case really awkward. The hoops to jump through for good typescript support while breaking the config down into reusable components...
Really wanted to like xstates but it felt like it was trying to own the entire app and was sacrificing usability for some version of academic purity. Also, feels like a huge marketing effort is under way to over fit it as a solution to any problem containing the word "state"; ie as a React state management solution..
I had a similar reaction after trying it a couple of years ago. I only needed a fairly straightforward FSM for a video player UI, and I like to properly understand any libraries I use. Xstate seemed to have waay too much going on.
I ended up writing my own in about 80 lines of typescript in less time than it would have taken to finish going through the Xstate docs. Doubtless Xstate is exactly what's needed for some projects, but I suspect it's unnecessarily adding to the complexity of many more.
Would love to know more about an example "simple embedding use-case" that you may have in mind.
We're not trying to force state machines as the solution for any state-related problem; they're not a panacea, but instead a useful tool for the appropriate use-cases.
And we're definitely making a huge effort to improve the types.
It doesn't require a library, it binds states and effects together, it is completely pure, and it can be implemented in most any language, preferably one with sum types and exhaustiveness checking. It works equally well with both complex, time-dependent business logic as well as code that controls UI states.
I love state machines for help in writing robust code. The act of laying them out this way forces you to consider lots of intermediate states that are ignore or forget. Plus, the logic stays away from all the gunk of the platform, state machines are trivial to test, and, once you get used to them, are easy to read.
I used it in the past and really disliked how it encouraged things like defining which functions to call by specifying a string with the function name. I would have much rather had it be more opinionated and require passing a reference to the function so it forces you to write code that benefits from typescript
There's many benefits in having a state machine configuration that can be serialized, especially if you can store that config in a database and load it at runtime. And actually xstate does allow you to pass the functions directly if you want to
Yes it allows strings or references to the function. My issue is that since the former is allowed, either people start using that method, or you have to start a “debate” within your team about patterns to avoid.
The serializability of your state itself has nothing to do with how events are bound to callback functions. If you serialize a string name of a function and not the source code of that function this buys you nothing. The source code itself is all text, so its serializable no matter whether the code is suitable for being statically analyzed
The ubiquitous waiting/loading/error(msg)/success(data)
Lots of frameworks and state management libraries already lean towards helpful design, in languages with async and ADTs as first-class citizens you've got a real leg up, but at the end of the day it comes down to preventing impossible states.
You can't have an error message when in the success state, you can't have success data while loading or waiting.
By preventing this at compiletime, it also means the UI can't ever show bogus data (i.e. it's impossible to have a success screen with the error message dangling at the bottom because somebody forgot to clear it)
Statecharts are language-agnostic, whereas your proposed solution appears to be tailored specifically for TypeScript or other strongly typed languages. In this context, Statecharts offer a more versatile and generalized solution compared to your suggestion.
general is not always better though. There's a tradeoff to other things like readability.
If a TS typed switch statement, say, offers the same guarantees of covering all options yet is just run of the mill JS that anyone can read and instantly grok, and covers 99% of usecases in run of the mill web dev product work, then to me that's an argument that it's the better solution.
I agree with you, as mention in another comment (https://news.ycombinator.com/item?id=35331199), that if you can cover all the cases with conditionals and you don't have nested states, don't use Statecharts. Just like distributed architectures are not for everything but more advanced use cases, so is Statecharts. It's a tool to help you manage great number of states, not just a couple.
Yes, sorry if that wasn't clear, but the transitions here are also typically restricted. For example, business logic may dictate that you can't go from waiting to success, you must go through loading and fetch fresh data. This prevents the problem of displaying stale data, at compiletime
As a huge proponent of Statecharts, I agree. If what you're doing is really simple and only have a few amount of states (as conditionals introduce those states no matter if you use state machines, statecharts or conditions), it makes sense to keep things simple.
But once you start having much more than that, or nested states, Statecharts makes it really easy to build reliable, performant and easy to change flows.
I can see that. Do you have some examples of common UI usecases which are the other side of the line here and for which statechart based code is better?
Ultimately every UI is a collection of various (often contradictory) states.
Example: Even typing into a text box on Hacker News is a new UI state: you have a different set of shortcuts, the keyboard works slightly differently, the browser has to keep track of the input if you suddenly press back and then forward again (some browsers maintain input in these cases), undo has to work within the scope of the textbox etc.
The more complex your UI becomes, the more contradictory states become, and need to be tracked. We're posting something over XHR/WebSockets? X amount of elements on the page have to be disabled, some state has to updated with save progress etc. We are logged in/logged out? We need to show a different state. There's an error in some part of the app? We need to block user from doing something and show errors etc.
If you can split those into actual states and allow interface and actions based on the current state, it becomes easier to reason about what is going on ini your app
> What everyday UI thing is better done with this than other methods?
All of them.
> Why is it better?
Because it specifies a workflow that actually matches what you want to do instead of employing a pile of ad-hoc rules that quickly become an unmaintainable mess.
If you have animations and async calls and multiple screens/pages, you have an application with application states which transition in response to inputs. That's what state machines are designed to handle.
Storyboards are state machines. Navigation components are state machines. UI binding layers like redux are state machines.
I hope you take this in good faith, but one often encounters claims in programming that a particular solution approach is universally better than alternatives for all usecases, often without accompanying concrete usecase examples, and the latter is exactly what I'm trying to dig out.
I am not saying you're wrong, but if you're right this would be the first case I've seen of it in my career so far :-)
User onboarding, setup wizards, feature announcements, or, more generally, the modeling (and maybe implementation) of incremental, progressive user flows. The only caveat is that they tend to require the machine's state to be persisted so that the user doesn’t experience the same state(s) again.
Truthfully, using state machines at runtime for the use cases above is sometimes too heavy. That distinction is actually a big input to what I’ve been building at Dopt.
Our platform lets you build state machines that are initialized per user in your application via our SDKs—you design the machines in the platform, and we provide APIs to let you transition them based on user interaction/input. We’ve taken a bunch of inspiration from Xstate and statecharts. I actually wrote up a blog about how we took inspiration from the latter https://blog.dopt.com/state-machines-and-their-influence-on-...
The real benefit for our project was being able to statically define, and therefore export the business process the app would follow. Once it's exportable, it's validatable and configurable.
IMO the JSON was hard to read, but we had a plan to build a GUI config builder that exported the state flows.
Ah yes, I can see this. I've also seen this kind of 'universally understandable spec of behaviour' attempted from the other direction with things like plain-language cucumber test definitions.
Emphasis on attempted, not sure I've ever seen it actually work as envisioned. Can see how bringing this directly into the code could remove some hurdles though.
Must say though I am a little skeptical on the readability / comprehensibility of the JSON format as you point out, but it's certainly a cool idea.
I use it for in component logic in vue. Used to have a collection of boolean or enum variables that represent the state of the component and change according to actions e.g. const isLoadingUser = ref(true). This works but often leads to unforeseen states being possible and just isn't very clear. In contrast, with xstate you define all states and legal transitions and wire everything up to send events, you can write guards that prevent transitions. I transitioned all my complicated multi stage forms to xstate and since doing so they've become substantially more robust. I highly recommend trying to out.
This is only incidental to your actual question (unless you’re into making widget toolkits), but have you seen the state diagram for a button widget[1]?
Almost everything can be written as a state machine and arguably it’s very useful to do so, for example let’s take a note taking app, you can probably list a set of transitions that can happen to change the state of the app. Keeping the state changes separate from the UI is a good thing. In a way redux is a state machine too. Try it out on a project and see if it works for you!
ChatGPT it, it’ll do a better job than I will and it’ll be relevant to your requirements.
It’s the same situation as redux though but with more formality and it’ll draw you a nice diagram too.
“Hey ChatGPT, can you show me an example of using XState typescript library to separate business logic from UI code in a computer program and discuss why XState might be useful for this purpose?”
The example it gives is really incredible… and obviously you can refine what it’s doing in context for your own understanding.
For example, the browser extension Redux DevTools implements "time travelling" by taking snapshots of the entire state on every state change. This is enabled by the fact that in Redux, state changes are separate from the UI. This same principle can be used for undo/redo history.
Another example is sending and receiving state changes from anywhere other than the UI, such as via WebSockets in a collaborative editing environment.
Got it, makes sense. At least in React this has been the long-understood default expected pattern though right? Like, you pass down props from something higher up. Is this a property of state machines or a library like this one?
Not trying to be pedantic I promise, I really want to see the value of this, but not sure I can without an example clearly comparing this against just a few if statements or something in pure JS, for something 'everyday'.
State machines make sense to me in a theoretical sense for something with a pretty complex tree of state transitions like a game but for the 99% run of the mill UI work you get in building web products I am not sure I see anything that brings value worth the tradeoff of having to learn yet another library.
That's true, keeping state and state changes separate from the UI is the default expected pattern in React, particularly with Redux. Or at least it was before hooks, which obfuscated the whole situation in my opinion.
And it's true that this separation of state and UI is just a general design pattern, which can be implemented with a plain object and switch statement, or a small class with a few methods - it doesn't have to be state machines.
So I'm with you in your conclusion, or skepticism perhaps - I prefer to apply the minimal pattern that achieves the same thing, rather than having to learn another state management library like Xstate or even Redux. But then again, I can see the value of such a library in a group/company environment, where you want everyone to learn and follow certain ways of organizing things, where new members can read the documentation, and understand how to add and change existing code in a predictable pattern.
This is not my understanding of React - can you elaborate?
Its original value prop was one way data flow and pure functional UI based on passed in props. It allows for side effects, to get certain things done pragmatically, but in my view does not encourage them in any way.
I've gotten good results from asking ChatGPT to give me Xstate representations of machines that I describe, very cool to copy-paste* into the visualizer as a starting point.
* Only issue is that with so much active development since the training cut-off it usually outputs deprecated structure / patterns.
Had the pleasure of working with XState at my last role. Where I found it greatly shined was being able to jump into a UI/machine I have never worked on before and contribute almost immediately after digesting the machine's definition
I've seen XState more and more, recently, what are the pros and cons versus other state management solutions? Anything that XState fails at particularly that potential users should be aware of?
The story for composition and reusability of multiple state machines is not good and it's not compatible with existing solutions for certain things (react query for example.) Supposedly this will all be fixed to some extent in v5 but that hasn't launched yet.
love how xstate makes implicit state machines explicit with a time tested (one might say Lindy) format. badly/ad hoc defined state machines are the source of so many bugs - David has been banging this particular drum the entire time i've known him (https://www.youtube.com/watch?v=HPoC-k7Rxwo&list=PLRvKvw42Rc...)
I wonder if we could build better implicit state machines if we could more easily visualize our imperative code by annotating it with visualization hints and parsing the AST. Also easier to debug/step-through this way.
I came across this code from 2-years-ago-me today:
{/* the fact that i'm doing this means I probably should have used x-state or something */}
{stages.filter(Boolean).length > 1 && (
<div>
WARNING! More than one assumption is true! {JSON.stringify(stages)}
</div>
)}
I wanted to learn about state machines so I searched for reading material here in hackernews. Found out about statecharts and used it immediately to rewrite a frontend app. What a fantastic development experience. It feels like I finally graduated from spaghetti coding to disciplined programming.
I always found it strange how state machines work so nicely in the small but tend to be difficult to scale up to large-scale, distributed systems.
Orchestrators like Kubernetes discourage designing state machines and transitions inside resource controllers. Instead a list of conditions associated with the object's status define a "phase" (state in k8s term). But there are no formal event-based transitions between states. Depending on the conditions and their tri-boolean values: True/False/Unknown, the object is effectively in a different phase.
Effectively, kubernetes recommends treating Resources like a game ECS - the controller is the system, the resource is the entity and the conditions are the state components.
We're using Xstate for state management instead of Redux and the like and it fits well for this purpose - chiefly I have an idiomatic way of defining which inputs are appropriate in different contexts.
The visualizer is a major selling point - especially if I need to explain something to non-dev people or someone new to the concept.
The one downside is that building state machines is an art in and of itself and I had to throw away my first implementation in favour of something simpler once I got the hang of it.
Add a description please. I get it's a state machine. I get states have state-tags, transitions can be ran via these transition-tags. What is your philosophy behind this library. Why should I give a fuck that states and transitions are strings rather than enums?
All I see here is a switch statement, the input is an enum, and the action that's taken is also determined by an enum.
any thoughts on a custom DSL for defining and verifying abstract state machines and then machinery for generators for different languages/libraries/environments/visualizations?
Just want to mention that we're very close to releasing XState v5 beta, which brings even more features to the state machines & statecharts you can create, and greatly improves the developer experience, but also makes it more usable as a general-purpose state management library (or orchestration "framework" if you want to consider it that), whether on the frontend or backend.