[HN Gopher] Xstate: State machines and statecharts for the moder...
___________________________________________________________________
Xstate: State machines and statecharts for the modern web
Author : thunderbong
Score : 128 points
Date : 2023-03-27 16:48 UTC (6 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| lenkite wrote:
| 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.
| instagary wrote:
| I tried XState for a mobile app.
|
| Pros:
|
| - The concept of states, events, and actions are an intuitive way
| to model state and the learning curve for me wasn't too bad.
|
| - I enjoyed using the visual editor inside VSCode and after
| setting up TS, the codegen was pretty cool.
|
| - The library supports parallel states.
|
| Cons:
|
| - The codegen was sometimes pretty bad for TS. There was some
| jank between the editor and the codegen, leading to frustrating
| DX.
| Rapzid wrote:
| 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..
| sanitycheck wrote:
| 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.
| davidkpiano wrote:
| 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.
| joshribakoff wrote:
| 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
| phpnode wrote:
| 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
| joshribakoff wrote:
| 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
| gooseus wrote:
| 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.
| paultarvydas wrote:
| There is a HUGE difference between StateCharts and State
| Machines, yet, the difference is subtle.
|
| StateCharts are the "Strucured Programming" version of State
| Machines.
|
| StateCharts use parental authority instead of inheritance (my
| words).
|
| A child state machine cannot override the operation of a parent.
| A parent can yank children out of their current states back to
| some known state.
|
| OOP and FP don't encourage this kind of thinking.
|
| The Big Deal concerning state machines is the "state explosion
| problem". Harel's StateChart notation conquers this problem.
|
| My reading of the original paper is here:
| https://guitarvydas.github.io/2020/12/09/StateCharts.html
| davidkpiano wrote:
| Hey, creator of XState here!
|
| 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.
| ar-nelson wrote:
| 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)
| [deleted]
| tobr wrote:
| 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?
| julenx wrote:
| For vanilla state machines there's also @xstate/fsm. In their
| docs[1] you have a feature comparison.
|
| [1] https://xstate.js.org/docs/packages/xstate-fsm/
| fallingmeat wrote:
| do you foresee sysml like diagrams in your roadmap? if you
| could do activity diagrams with swimlanes...dude. that'd be
| huge.
| davidkpiano wrote:
| 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.
| satvikpendem wrote:
| 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?
| epolanski wrote:
| Verbosity, complexity. It's probably overkill for 99.9% of the
| things you imagine.
|
| Learning curve is more similar to rx-js or fp-ts than a state
| manager.
| phpnode wrote:
| when you do get over the learning curve it's really excellent
| though.
| brigadier132 wrote:
| 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.
| davnicwil wrote:
| The readme gives the example of a traffic light to demo a state
| machine usecase.
|
| Fair enough, but what's a rudimentary usecase for 'the modern
| web'?
|
| What everyday UI thing is better done with this than other
| methods? Why is it better?
| epolanski wrote:
| The usual "the examples are so trivial to implement safely in
| other ways that I don't see the benefits".
| andy_ppp wrote:
| 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!
| davnicwil wrote:
| > Keeping the state changes separate from the UI is a good
| thing
|
| Why? Can you give an example?
| lioeters wrote:
| 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.
| davnicwil wrote:
| 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.
| lioeters wrote:
| 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.
| the_gipsy wrote:
| React expects and recommends you to use side-effects.
| davnicwil wrote:
| 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.
| the_gipsy wrote:
| https://react.dev/reference/react/useEffect#what-are-
| good-al...
| andy_ppp wrote:
| 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.
| [deleted]
| sonicrocketman wrote:
| 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.
| davnicwil wrote:
| 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.
| simplotek wrote:
| > 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.
| davnicwil wrote:
| 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 :-)
| joemckenney wrote:
| 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-...
| mananaysiempre wrote:
| 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]?
|
| [1]
| https://web.stanford.edu/class/archive/cs/cs103/cs103.1142/b...
| (requires pointer and not touch input)
| dakom wrote:
| 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)
| joshribakoff wrote:
| I think this is solved more generally with a discriminated
| union in typescript.
|
| A state machine is the wrong solution unless you also need to
| restrict transitions themselves in my opinion
| capableweb wrote:
| 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.
| davnicwil wrote:
| 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.
| capableweb wrote:
| 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.
| davnicwil wrote:
| I think a switch or an if/else expresses this really clearly
| though and also prevents impossible states.
| capableweb wrote:
| 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.
| davnicwil wrote:
| 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?
| henrydark wrote:
| the original paper by David Harel has a marvelous long
| example of his own digital watch, highly recommended
| davnicwil wrote:
| thanks, I'll check this out!
| strangeattractr wrote:
| 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.
| illiarian wrote:
| 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
| sonicrocketman wrote:
| 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.
|
| Shame the project died.
| yagodragon wrote:
| 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?
| jrvarela56 wrote:
| Sounds awesome, given it died could you publish the source?
| Would be great to see how something like this is structured.
| sonicrocketman wrote:
| Unfortunately no. It was proprietary.
|
| (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.
| jjclane wrote:
| We're currently building something that sounds
| superficially similar, would you be interested in having a
| chat?
| sonicrocketman wrote:
| Sure. Check bio. Email in blog's about page.
| kidfiji wrote:
| 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
| a-dub wrote:
| any thoughts on a custom DSL for defining and verifying abstract
| state machines and then machinery for generators for different
| languages/libraries/environments/visualizations?
| lerchmo wrote:
| I have been using this XState alternative
| https://github.com/StoneCypher/jssm
|
| I find the DSL much easier to wrap my head around than the json
| format of Xstate.
| swyx wrote:
| 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...)
| a4isms wrote:
| " _Any sufficiently complicated model class contains an ad-hoc,
| informally-specified, bug-ridden, slow implementation of half
| of a state machine._ "
|
| -a former colleague
| baq wrote:
| "I don't need a state machine library" - everyone when
| starting a new project
| xavdid wrote:
| 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> )}
|
| One day I'll learn...
| vaughan wrote:
| 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.
| gherkinnn wrote:
| Then why not just make it explicit?
| rektide wrote:
| Having really good tracing/observability on an explicit
| state machine like Xstate would be such a great superpower.
| mattgreenrocks wrote:
| I need to write a post on this, but I've been using this pattern
| to great effect:
|
| https://gist.github.com/andymatuschak/d5f0a8730ad601bcccae97...
|
| 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.
| mhoad wrote:
| Seems you're not alone. This was ripped out of Google's Stadia
| platform I believe but might be wrong
| https://github.com/google/hierarchical-state-machine.dart
___________________________________________________________________
(page generated 2023-03-27 23:01 UTC)