102 comments

[ 2.8 ms ] story [ 176 ms ] thread
Isn't the useReducer hook already an interface for building state machines? The clue is in the signature of reducer functions, the signature as exactly the same as textbook state transition functions...
The general use reducer is a infinite state machine, while this is a finite state machine. The normal reducer can allow your app to be in any state, while finite state machines let you list the only possible states (like ON, OFF, UNKNOWN; or DRAFT,SENT,BOUNCED) - and offers state transition functions to move between them that can refuse to move unless certain conditions are met.
It's only a FSM if the users litter a guard function all over the place, otherwise this is just some deeply nested non-sugar on top of useReducer.
This still mostly sounds like a useReducer. With TypeScript you can guarantee the built code only respects a finite state shape. Depending on the dispatched action, you can just default to returning the previous state - refusing to move unless certain conditions are met.
Yes, you can. The vast majority of developers don't. That's the difference: enforcement.
Yeah, one can definitely build a finite state machine on an infinite state machine, that’s essentially what the library helps you do. It providers a framework to do so with some boilerplate taken care of.
I can't get over how amazing hooks are and how resistant I initially was to them.

They encapsulate logic so well and do an amazing job being this isolated data/event source.

I find that I reuse them far more than I reuse components.

They fit so well into React it's crazy.

But they're also soooo idiosyncratic which makes them rather ugly on a general level.

They also come with many restrictions and rules on correct and efficient usage, not all of them checked by ESLint, that make the learning curve a bit brutal.

But yes, insanely useful once you get the hang of them, they enable forms of abstraction and composability that you'll sorely miss when working in other UI frameworks.

Aren't there proper TypeScript bindings which catch (more) cases?
Interesting. I have the opposite experience, the more I use them the more clunky they feel.

* If there’s only useState, then everything is mostly fine. All is good.

* To avoid unnecessary re-rendering you’ll have to move callbacks into useCallback. These callbacks then also need to specify all of their dependencies. So many lines of code that are merely noise.

* They encourage having state locally in the component which often breaks down the moment you slightly tweak the design. So much refactoring!

* Dealing with any other (callback-based, stateful) API is confusing. If you want something to execute once initially you can use useEffect with an empty dependency list, but note that then there’s no way of accessing updates values (in callbacks) and everything will reflect the initial state. Often you’ll have to use useRef just to keep track of the current state. Or maybe useState?

* Debugging is painful. You forget one value in your dependency list and the weirdest things happen. Suddenly you have callbacks running in different rendering scopes with different values.

They feel very “brittle”: Once it works the code looks pretty, but when there’s a slight change of requirements you need to rethink everything.

> To avoid unnecessary re-rendering you’ll have to move callbacks into useCallback. These callbacks then also need to specify all of their dependencies. So many lines of code that are merely noise.

I really like Vue 3’s Composition API as a much easier-to-use (IMO) implementation of hooks.

It’s conceptually very similar to React Hooks, but the method that defines them only runs once, when the component is instantiated. So straight away that removes any worries around conditionals and loops.

Instead of a plain value/object and an update function, the “hooks” instead return a reactive object that tracks it’s own dependencies (which are also all reactive objects). That removes any need to manage dependencies yourself, or for any memoization.

The only downside is that because a plain value (like a number or something) cannot be reactive, it has to be wrapped in an object and accessed via a “.value” property, but this is only relevant in the setup function. Anywhere else, like the Options API or in the template, accessing the value like a value will just get transparently proxied to the object.

The entire concept of reactive objects brings back memories of extreme pain from KnockoutJS days (and terrible performance in complex scenarios to boot). I prefer just working with plain old data structures.
I prefer working with just plain old setup functions, that are called once.
I find React and Vue to be different expressions of the same core concept in a way. Althoguh I do prefer the functional nature of React, reactive objects are a lot less boilerplate, even if it might be a bit slower. The composition api itself is actually I think where it shines, because the options API is very mediocre in my opinion.
Is it actually slower though? I feel like that’s Vue’s entire marketing strategy. “We’re just as fast and small as React but include way more out of the box and it’s easier to grok for new people”.
> You forget one value in your dependency list and the weirdest things happen.

Don't you get eslint warnings right in your editor?

If you're using VS Code there's an extension for that...

Not only this but I need the freedom to control the dependency list for an effect. Sometimes it isn't as simple as "every variable being referenced inside the effect."
that's a bit confusing. While you can sometimes get away with leaving out dependencies, and then you don't have to useMemo or useCallback as often, it seems like there is a much larger downside to having potentially stale values in your effect. Leaving them out to me would result in the potential for weird future bugs and race conditions.
> If you want something to execute once initially you can use useEffect with an empty dependency list, but note that then there’s no way of accessing updates values (in callbacks) and everything will reflect the initial state. Often you’ll have to use useRef just to keep track of the current state. Or maybe useState?

I'm not sure if I understood what you mean, but note that useState accepts not only the updated state, but also a function that takes the current state as argument and return the updated state.

In other words, instead of something like this

useEffect( async () => {

  ...

  const someValue = await someFunction();

  setState({...state, someValue });
}, [... ] );

you should use something like this

useEffect( async () => {

  ...

  const someValue = await someFunction();

  setState( currentState => ({...currentState, someValue }));
}, [ ... ] );

(the example is with async but it is the same for regular callbacks)

Note that you shouldn’t (can’t?) actually pass an async function directly to useEffect, since it returns a promise: https://github.com/facebook/react/issues/14326
Not true, see Gaeron's direct reply in the thread you linked to, that's been the standard for 3 years and works like a charm - useEffect specifically is the hook for handling async functions ("impure effects").
Notice the words "pass to useEffect" in the parent comment, and the code snippet in the grandparent they are commenting on. The type of the callback function that useEffect accepts is () => void rather than () => Promise<unknown>. What's not true about that?
I reworded it to make it a bit more clear (at least, I thought so) — "pass an async function to useEffect", meaning the argument supplied to useEffect itself, rather than "use directly within" which is ambiguous. Re-added "directly" to hopefully make even more clear. Sorry for the confusion!
you're right, good catch. Usually it's the TS compiler that prevents me doing that.
I usually use an IIFE to encapsulate the async block inside the useEffect. Works like a charm!
True, you cannot do that in useEffect, it will throw you error. And probably your linter will tell you. You can't even use your own or third party hooks in side useEffect. This also throw error I don't know why?
Because you could be nesting hooks, which would bring all sorts of weird behavior.
Seconded. Plus, you start writing wrapper functions for standard Javascript APIs (e. G. window.setInterval) to force them into Reacts data model.
... which is a good idea anyway because you want your code to be testable.
...this makes no sense at all.
Why would you be down voting this? Parent's statement is completely nonsensical. If you wanted to test a function irrespective of window.setInterval's implementation you would just PASS window.setInterval as a parameter to that function. You would NOT add a wrapper function for window.setInterval because it is completely useless.
The function you’re talking about would be a react functional component. I don’t think passing window.setInterval in as a prop to a react component is a particularly common approach to achieving component testability - though by all means give it a try.

I don’t think it’s ‘nonsensical’ though to suggest that it’s a fairly common practice in testing to not want your unit tests to actually set up recurring callbacks.

Isolating an external side effect to make it testable, as well as to align its abstraction with the one that your framework uses is a common pattern anyway.

I’m not sure why this angers you so much?

Why would you do that? What’s wrong with this?

    useEffect(() => {
      const interval = setInterval(myFunc, 1000)
      return () => clearInterval(myInterval)
    },[])
I think one of the keys to React is not to fear the boilerplate.

If you find yourself typing the same code too often, there’s probably a higher level abstraction that you should find, way above the level of setInterval.

I find coders often try to DRY up low level code to the detriment of actually dealing with their _application_ architecture.

In general, you should use the tools as provided, boilerplate and all, and then focus your real brain energies on the specific domain problems that only your application needs.

Creating thin wrappers on external tools to squeeze the last tiny bits of DRYness is not only usually a waste of time, but it makes your code unreadable. People know how setInterval and useEffect work. If I see useInterval I have to go read your code to know what’s happening. Especially if you made it configurable. And people _will_ tend to add configuration to these wrappers over time as needs change.

in my experience is better to have customs hooks like useTimeout (react-use library is awesome) so is easier to test. Mocking hooks feels nice, mocking the browser API not so much...
My experience is mixed but mostly positive.

- Agreed about useState (and useReducer). They’re simple and they get the job done.

- Agreed about useCallback as well, it’s a lot of boilerplate.

- State in general is a hard problem to deal with, but I’m not sure how hooks in particular encourage it? Class components had the same issue.

- IMO, useEffect is a huge footgun. I understand how it works, I understand how closures work, but it is by far the biggest source of hook-related bugs I’ve experienced.

- This eslint plug-in will solve your dependency list woes: https://www.npmjs.com/package/eslint-plugin-react-hooks

+1 for that eslint plugin, I was just idly wondering if something like that existed earlier this week. Thanks.
>You forget one value in your dependency list and the weirdest things happen.

This is what I hate in angular1 , there are at least 3-4 ways you can mess up a simple data-binding and there is no error or warning to inform you that something is not right so you waste a lot of time to figure it out.

I used react a few years back but it seems that this days it got hyper complex.

As far as I can tell you can just not use hooks, if you (and the rest of your contributors) agree.
(comment deleted)
Regarding useCallback, you can probably skip it in most cases, unless the extra renders are a problem for you.

Linters should help with missing dependencies.

Yes! useCallback is an optimization and using it prematurely can be problematic.
> You forget one value in your dependency list and the weirdest things happen

Really? I've been working with hooks for months, with code in production, and I hardly ever pass more than one or two items to the dependency list. The only time I really had any trouble with dependencies was when I was updating appContext inside a hook.

> You forget one value in your dependency list and the weirdest things happen.

There's an eslint plugin specifically for detecting this, which should probably be considered mandatory.

> Debugging is painful. You forget one value in your dependency list and the weirdest things happen

This should never happen. The only downside to hooks is that the eslint plugin is _required_, not optional. It is not worth the pain without it, but you'll be in a Whole New World once you do it. Since you can auto-fix every time, it'll become second nature and you'll never forget a dependency again. You can focus on getting your application to work.

You can use OOP or whatever you want to build your abstraction and then surface that as a hook, rather than using hooks as a way to do all your logic. This is similar to how there are npm packages that just import some other js package “foo”, wrap its api in a react component, and reexport it as react-foo

I’m curious what you think of https://rxstore.dev/ it basically is a pattern I tried to outline where you write logic with rxjs and it tries to bridge the gap to react/hooks for you. I address some of your points in the faq.

>but note that then there’s no way of accessing updates values (in callbacks)

This is by design. When you start making a component worry about what it needs to do on an update, rather than just rendering its data, you are introducing a whole new level of complexity. That's why class based React components could turn into these unwieldy confusing things where you never knew for certain what was going to render every time, because the shouldComponentUpdate method gets stuffed full of business logic.

that's pretty much how I feel, too. I really love the flow of hooks and I write a ton of them -- majority of the logic I write tend to end up in hooks and I really love that.

There's something to having a function that can hold state or load its state on mount, etc.

Unsurprising it took people almost a decade to discover "depend()" from Meteor.
React hooks allows for reusable bits of logic. Reusable bits of logic are a great idea for a UI framework. But react hooks is a terrible implementation of that idea.

You're no longer writing plain javascript, you're writing in some terribly verbose language where everything from variable declarations to function expressions needs to be wrapped, making it hard to integrate with this party APIs and small details can have a big impact on performance.

Both vue 3 and svelte show it can be done better.

It's a good abstraction (definitely a massive improvement over the awkward classes) but it's built on so much react tech that you need limits which don't make sense (all the rules you need to remember) and when things break you're in for a world of pain.

You can definitely have "hooks" that are just normal js functions without limits.

My favourite public example of this is definitely solid.js (by ryansolid).

In general, I'm grateful to react / facebook for bringing in the spotlight the hyperscript way of doing applications and pushing tons of developers to develop similar libraries - but technically react is bloated and a ugly corporate mess.

Similar considerations for redux and elm.

So I think this is basically an abstraction on top of useReducer which itself is kind of a state machine pattern.

It’s a little verbose more my liking but I think it could be powerful for large apps with lots of states.

This is a Finite State Machine meaning there are a limited set of states and state transitions allowed. useReducer doesn’t have any of the state/transition validation logic which makes it an infinite state machine. Both are very useful. Different use cases though.
Thanks for including the comparison to xstate; that's where my mind immediately went when I first saw this. Are there any plans to create a visualizer, similar to what xstate has?
Of course, XState is a huge inspiration and the golden standard of State Machines for JS. I visualizer is not on the short-term plan, but there’s a “verbose” mode that helps with debugging.
I'm excited to try this. I've tried xstate many times and always struggled with the typescript, if this Just Works I'll be v happy
We're constantly working on improving the TypeScript experience with XState; there are some new things like `createModel()` that might help!
Oooh createmodel is new to me - will check it out!
Oh, I need to try that too!
Actions defined in options are the killer for me at the minute (and guards, but actions are more important generally). I've had to resort to using enums (+ type guards + coercions) for everything. Have you any idea on if/when can get these in the model API? At the minute the API is nice, but not particularly useful with only context & events. The type gen library seems to be a sticking plaster more than anything else: it's clever, but building an NPM package on the fly seems super fiddly (and flat out doesn't work with newer Yarn setups), so movement forward on inference without it would be great
Can anyone provide an example of a situation where something like this is a pragmatic solution? Please also provide the drop dead simple version too and explain why this would be more elegant and intuitive in comparison.

Types of examples I am not interested in seeing:

- a DropDown list using a state machine (or something similarly simple that’s been solved a million times in a simple way).

Edit:

I just think stuff like this is too weird and dangerous to evangelize, and I’m at my wits end in dealing with simple components that have been over abstracted.

Lots of good examples here: https://xstate-catalogue.com/

It seems like you're just unfamiliar with state machines. They're not a "new, weird abstraction", they're a core concept of computer science in general, and it can be argued that most code we write is itself an abstraction on top of state machines; not the other way around.

For more information on state machines and statecharts, I recommend reading this great resource: https://statecharts.dev/

No, I know what state machines are. What I’m contesting is the argument that this is an intuitive way to structure code. It could be intuitive to reason about, or even white board the states your application can be in, but this doesn’t look like concise and elegant code.

From your examples: https://xstate-catalogue.com/machines/confirmation-dialog

I’ve written a million confirm-dialogs in my lifetime, and that example is one of the most convoluted things I’ve ever seen.

Guys, just because it’s in Computer Science, doesn’t automatically mean it’s a more sophisticated solution. The term for this is over-kill.

Nobody's forcing you to use this library. Please take your negativity elsewhere.

EDIT: Okay, didn't mean to interpret criticism as negativity; I apologize.

Fair enough, but the echo chamber is why stuff like this gains prominence. I’m abrasive for sure, but I need to see some push back on these ideas in all of these threads.
Criticism is not negativity. It's valuable when someone in the room challenges ideas and pushes back over-abstraction (if done respectfully and constructively).
Can you even describe the use of a state machine for UIs as "over-abstraction"? To me it seems more like an "under-abstraction", since one usually compiles a more abstract definition into a state machine - often a regular expression or some kind of grammar.
Was just discussing this with a friend this morning. It might be better to describe it in terms of trade-offs versus the overuse of the ‘over-abstraction’ allegation.

So, in a lot of cases in the UI, we are turning what is very recognizable and traditionally understood code into a new form to fit the state-machine world view in a more 1:1 way.

Again, it’s a trade off. What did we gain from this transformation? I don’t think we get enough back from this trade to structure our code in this new way.

Or to be clear, why would I trade my ability to read a few simple fetch calls that update a simple DOM element for this version? What is the outsized return here?

Just my 2 crypto.

Did you start programming with OOP or Functional programming? Did learning how to programming in the other form seem difficult to understand why you would do things that way, or seem obtuse vs just using the ways you already know?

I think using state machines is just a very different way of looking at code, and it does take some mind bending to think that way; but once you do, it's much easier to blend the code together the same way you'll see imperative and functional code blended together these days and not think twice about it.

(comment deleted)
How are state machines "a very different way of looking at code"? It's basically assembly with jumps as state transitions. A decent programmer already understands this from assembly lessons (and avoids doing it by hand, leaving it to the compiler).
I think your problem is that many things don’t have to be a state machine. And you’re right. It’s simply not a good idea to create a confirmation dialog like that.

However, some things just _are_ state machines. You don’t usually see them in the GUI (except for example wizards), but many parsers can trivially be implemented with state machines.

I actually believe there would be fewer UI bugs if more UI was modeled as a state machine. After all, all diagrams and design is often based on "when X is shown and Y is pressed, we go to Z". But in code that's expressed in a way that makes it possible to have a mismatch between multiple half baked states.
(comment deleted)
> It seems like you're just unfamiliar with state machines.

You're saying that he doesn't know computers? Computers are state machines. Presumably he used one to post his comment.

I’ve needed to construct a chatbot-esque UI on more than one occasion. I used a state-machine on the second time round and it simplified work considerably!
I think using a state machine can make the resulting code more elegant in some cases by coupling state changes and effects. But that’s not why they are useful. In my experience, they have been useful because they help map (even mentally) all individual possible states a piece of code can be in, and how the code should behave in each case. In the process, I usually discover and deal with more edge cases, more combination of states and behavior, and as a consequence the code is bigger/fatter. But also more robust.
This is important, and it's probably the main reason I like using them so much. But then surely the comeback is that what I'm then attempting to do is produce a diagram in code, when I could just write the diagram on paper, check it, then write some (possibly simpler) code. Not in all cases, naturally, but often?
Yeah, I think you articulated that better than a lot of us here. That, in a sense, your diagram becomes your psuedo-code, or even further, your actual implementation.

My initial defensive impulse to this topic was mostly to take a stand against frontend code that is becoming increasingly obtuse over the years.

Auth logic or similar. Somewhere area in app that may be genuinely complicated and can't go wrong, where you need granular control over a set of states which often have sub states, and where there are things like remote requests to different services occurring.

And you can just write this stuff as basically lots of nested if statements but...

As an actual example, I have a state machine in an app I work on:

1. check if there's a session (if so goto 4). 2. If not go to email input. 3. On submit go to password input. 4. authorized. However: 2 and 3 and some of the time 1 have network requests, so handle those. Password can be entered wrong three times (error message changes accordingly, oh also no. retries are modifiable at admin level), which bumps user back to 2 (error message changes accordingly). User may have turned on PIN security, if so that will be an extra step before allowed into app. They may also have it turned on, but not one set, which is a slightly different flow. And they can turn it off/on from within app. Oh and biometric, same deal as PIN. Also can drop back to username/pword instead of OTP in some circumstances.

That's most stuff I think off top of head. That's a hierarchical state machine written using XState. It's easy to test (UI is seperate from the machine, and I can just plug in API functions for the remote calls). It's relatively easy to read (xstate API produces stuff that reads like a diagram, it's easy to explain what's going on). It's easy to add/remove states

That’s actually a good use case, thanks for sharing.
Just to note, I think I do agree with you. With the above flow, it's behind an interface that just provides update functions, the current top-level state, and the values that are updated by it (the user object the remote calls return from my Auth provider, for example). All the machinery is completely hidden: I had previous versions where it was more exposed, but it asks a lot of every other dev I work with to learn yet another complex API.

There are other potential uses for in my apps (ex. backing a numeric input made up of multiple inputs for PIN/OTP that needs to track focus state + a few other things), but I've been slightly wary about introducing more xstate code.

I feel like:

- explicit state machines are extremely useful.

- they're very well understood by embedded/game devs, but not so much by web GUI devs...

- ...but the latter have an innate understanding due to much of the things they build often being implicit state machines. And those things are often fine as they are.

- adding an explicit machine greatly increases granular control at the expense of [complex] boilerplate.

- the simple fact that they are so explicit (and that you get one state at a time) means state machines aren't some silver bullet for UI, and they get complicated really fast due to state explosion.

- I think any game dev would be able to explain why you don't just use them for everything, and I find it slightly strange that there's little input from game/embedded devs when these things come up on HN -- maybe I'm just assuming there's some overlap of knowledge when actually there isn't.

- heirarchical state machines and state charts fix the state explosion issue, but then bring with them a more complex API that needs to be learnt.

I'd say, for web/JS, XState is gold standard and it's a fantastic library. Explicit state machines are very useful, but writing ad-hoc machines that do anything useful isn't particularly pleasant in JS; XState, with its serialisable object configuration, is pretty easy to explain.

It has the advantage of being completely agnostic w/r/t front-end library, which I think is critical. I can shove it behind an interface, and if necessary replace it with something else pretty easily. I'm somewhat uneasy about having it directly tied to [in the OP's case] React, because I'm in agreement that it's baking clever, complex code right in (yes, for anyone disagreeing, I am aware state machines are a simplification. So is Redux. So are observables. Hell, so is recursion. Anyway).

I think the value of a state machine is treating all of the associated states in a single conceptual model or level. You can definitely do everything directly in a bunch of if statements and tracking state in separate variables, but that opens yourself to more potential bugs, higher maintenance cost, less big picture understanding, and even larger performance costs (separate codes means the compiler can't optimize as well; means hardware caches aren't as effective; there's more overhead to run separated functions; etc, etc).

Here's hopefully a more apt example: imagine wanting to have a multiple document interface (MDI) that shows a bunch of 'window' like views. Some examples of the idea are react-grid-layout, golden-layout, react-mosaic. Each window can be minimized, maximized, moved, flashed, and closed ... all with flashy animations. You could create a whole bunch of components that allow you to capture all the different states and toggle them in different components, and effectively have the MDI business logic sprinkled all through out YOUR code, not just the 'library' code. Alternatively, you could use a state machine that captures all the states in a single spot and manages that all efficiently and safely, you just call an api to trigger state changes throughout your code.

As some other comments have mentioned, this is a fundamental part of computer science. You can get by without knowing it cause you can write a bunch of ifs and state logic all over the place. If you only know how to use a hammer, ya you could still hammer a screw in. Understanding how different data structures of different patterns fit together provides you new tools to do things in different ways when appropriate. Learn how to manage complexity, not fear it, cause there's nothing dangerous about state machines. In fact, I'd argue that anyone that thinks state machines make things more complex just isn't looking at a large enough scale because state machines should make things less complex by wrapping all the complexity inside them (when used appropriately).

I’ll concede I came with a contentious tone, which is probably resulting in a similar retort by a few of you that is mostly using appeal to authority to justify your claims (eg just learn computer science bro, you must not know it).

Sporadic business logic/spaghetti code is a problem in any application. State machines will not magically avoid this. In fact, it should be just as susceptible to it. When sphagetti code shows up under a complex architecture like this, you could be in a world of hurt. There won’t be a few if-statements for you to unwrap, but instead a maze of cascading state updates. Another common thing I’ve seen is the granularity of capturing any and all state updates, and then some. It’s very tedious.

Anyway, I’ve been dead wrong about 70% of things in life before, so I’d be happy to look at a non trivial app written with xstate if anyone’s got a repo.

:-) I feel you. I like to think that state machines are used where they are useful (in some games, process orchestration, generators, embedded systems, etc.). The question is more where else can they be useful given that the low hanging fruits have already been harvested.

I am using state machines to write reactive applications: https://brucou.github.io/documentation/

So far I have to say that it is a mixed experience. There is a cost to the abstraction and the indirection (that is fairly well known easy to describe even if we rarely do so due to some self-imposed no-negativity bias or having some interest in the game), and then there are the benefits that are less easy to describe because they depend highly on the nature of the problem that you are addressing.

I implemented a Medium clone application (https://codebase.show/projects/realworld) with state machines. The result is 47Kb (brought down to 39Kb after compiling the machine and other optimizations) vs. 70Kb for the Vue implementation or 160 KB for the React/Redux one (that implementation piles abstraction over abstraction in the form of libraries and pays the corresponding price). I would count that as benefit. Also cf. https://brucou.github.io/documentation/v1/tutorials/index.ht... for the full pitch.

The high-level machine is that one: https://brucou.github.io/documentation/graphs/real-world/rea...

At this level, you can follow the routing of the app. Every route is a compound state. If you open it, you see the details of the behavior. The full machine (post refactoring) is like this: https://brucou.github.io/documentation/graphs/real-world/rea...

So one advantage is that with those graphs, it is easier to onboard new folks arriving to the codebase. They just have to follow arrows to know what the code is doing in response to a series of inputs.

I could continue but with all that said, the Hyperapp implementation of the same application is 27Kb and also fairly simple (even if arguably not as simple) to get into. So there isn't a clear-cut ex-nihilo benefits to state machines here. In fact if you would have implemented the same application as a MPA instead of a SPA, for most, if not all of the pages, using a state machine to model the behavior is simply overkill. Most of the job and value of the machine in my example is to do the client-side routing done in the machine (so no extra library cost).

Anyways the bottom line is the tool is useful but you have to figure for what, and where is the value maximized. The obvious cases have already been figured out.

How does this work with RxJS or reactive pattern?
It’s not directly related - this is a thin layer on top of useReducer and useEffect, so it’s more directly tied to React. But take a look at XState
It’s interesting to me how certain design patterns get rediscovered over the years. A few years back FSM’s were a dirty word for crusty Java devs. I myself avoided them for that reason. Then one day I had a perfect use case for one and took the plunge and was amazed by how useful a pattern it is. I’m embarrassed at how long it took me to get hip to it. I’m writing a feature at my job right now that makes heavy use of FSMs. Very testable and the nature of the pattern means you can have a lot of confidence in your state being valid. I highly recommend trying it out even if you don’t adopt this hook or xstate (you can make a FSM fairly easily)
Finite state machines will never not be useful for the same reason regular expressions will always be useful. They're a supremely simple model of computation, making them easy to reason about.

The problem with state machines in Java is not the state machine's fault, it's Java's fault.

I feel like you are refuting a point I didn’t make. I was just talking about how we re-discover patterns. My feelings on Java have nothing to do with it.
love the logo
Hahaha, reused from a video on my YouTube channel, was created in 5 minutes on Apple Keynote
(comment deleted)
As someone that works with finite state machines, I can’t begin to describe how annoying I find it when everyone needlessly reduces the name down to just “state machine”. To me, it makes it seem like the people making “state machines” don’t understand the whole “finite” part and thus don’t understand half the point.
If the term "finite-state machine" were used instead, there would inevitably be people making comments like you, but instead complaining "this isn't finite, you have extra data in the machine that can be infinite". The pedantry isn't helpful.
No, your disingenuous disregard is not helpful. FSM is literally the name of it. Not all FSM's store extra data, that is only a recent concept from things like xstate.
why does this need to be a hook?