Search code examples
javascriptreduxstate-machinexstate

What is an actual difference between redux and a state machine (e.g. xstate)?


I am working on investigation of one front-end application of medium complexity. At this moment it is written in pure javascript, it has a lot of different event-based messages connecting few main parts of this application.

We decided that we need to implement some kind of state container for this application in scope of further refactoring. Previously I had some experience with redux and ngrx store (which actually follows the same principles).

Redux is an option for us, but one of the developers proposed using a state-machine based library, in particular the xstate library.

I've never worked with xstate, so I found it interesting and started reading documentation and looking at different examples. Looked promising and powerful, but at some point I understood that I don't see any significant difference between it and redux.

I spent hours trying to find an answer, or any other information comparing xstate and redux. I didn't find any clear information, except some articles like "get from redux to a state machine", or links to libraries focused on using redux and xstate together (quite weird).

If someone can describe the difference or tell me when developers should choose xstate - you are welcome to.


Solution

  • I created XState, but I'm not going to tell you whether to use one over the other; that depends on your team. Instead, I'll try to highlight some key differences.

    Redux XState
    essentially a state container where events (called actions in Redux) are sent to a reducer which update state also a state container, but separates finite state (e.g., "loading", "success") from "infinite state", or context (e.g., items: [...])
    does not dictate how you define your reducers - they are plain functions that return the next state given the current state and event (action) a "reducer with rules" - you define legal transitions between finite states due to events, and also which actions should be executed in a transition (or on entry/exit from a state)
    does not have a built-in way to handle side-effects; there are many community options, like redux-thunk, redux-saga, etc. makes actions (side-effects) declarative and explicit - they are part of the State object that is returned on each transition (current state + event)
    currently has no way to visualize transitions between states, since it does not discern between finite and infinite state has a visualizer: https://statecharts.github.io/xstate-viz which is feasible due to the declarative nature
    the implicit logic/behavior represented in reducers can't be serialized declaratively (e.g., in JSON) machine definitions, which represent logic/behavior, can be serialized to JSON, and read from JSON; this makes behavior very portable and configurable by external tools
    not strictly a state machine adheres strictly to the W3C SCXML specification: https://www.w3.org/TR/scxml/
    relies on the developer to manually prevent impossible states uses statecharts to naturally define boundaries for handling events, which prevents impossible states and can be statically analyzed
    encourages the use of a single, "global" atomic store encourages the use of an Actor-model-like approach, where there can be many hierarchical statechart/"service" instances that communicate with each other

    I will add more key differences to the docs this week.