When you grok state machines, a new world of computational beauty and software design opens in front of your eyes. Things that used to be hard become easy. The impossible becomes possible. And you turn into that annoying team member.
"Damn it! Stop it with these state machines. Nobody understands your code"
It's true, nobody understands what the heck this means:
const loader = createMachine({
id: "loader",
initial: "fetching",
states: {
fetching: { on: { FETCHED: "fetched", ERROR: "error" } },
fetched: { on: { RELOAD: "fetching" } },
error: { on: { RETRY: "fetching" } },
},
})
But if you show it like this:
Now we're talking! This is a smol state machine that represents a data loader with 3 states – fetching, fetched, error – and transitions between them.
That's all a state machine is – a collection of states with transitions. Best represented as a state chart like above, easily drawn by hand, popular building blocks for complex systems based on the actor model of computation.
You can think of ant colonies, human cities, and generative video game landscapes as examples of actor-based computation with state machines. An exploration for another day :)
You're already building implicit state machines
The one thing you should take away from this article is that you are building state machines even if you don't realize it. State machines are everywhere.
Take this (pseudocode) React component for example.
It uses React Query to load some data, a showMore
state to choose how big a list to render, and attempts to handle various edge cases.
function pseudoCodeExample() {
const { data, isLoading, isError } = useQuery()
const [showMore, setShowMore] = useState(false)
function onShowMore() {
setShowMore(true)
}
function onHideMore() {
setShowMore(false)
}
return (
<div>
{isLoading && <Spinner />}
{!isLoading && !data && <NotFound />}
{!isLoading && isError && <Ooopsies />}
{!isLoading && data && (
<>
<List data={data} count={10} />
{!showMore && <Button onClick={onShowMore} />}
</>
)}
{!isLoading && data && showMore && (
<>
<List data={data} count={data.length} />
<Button onClick={onHideMore} />
</>
)}
</div>
)
}
You can imagine how a component like this grows over time. The bugs were whack-a-mole, new exceptions and edge cases were added, and nobody took the time to step back and think "Is our approach wrong?"
Can you spot the bug? Revealed at the bottom.
Complex conditionals like that break my brain and When your brain is breaking, try XState ✌️
Explicit state machines feel like more work
Adding an XState state machine to that component feels like extra work.
The state machine itself is not simple:
const stateMachine = createMachine({
id: "machine",
initial: "loading",
states: {
loading: {
on: { ERROR: "error", LOADED: "loaded" },
},
error: {},
loaded: {
on: { SHOW_LESS: "show_less", NOT_FOUND: "not_found" },
},
show_less: {
on: { SHOW_MORE: "show_more" },
},
show_more: {
on: { SHOW_LESS: "show_less" },
},
not_found: {},
},
})
And then you have to write all the glue code to drive this state machine. React Query and XState don't natively talk to each other. Yet 🤞
Is this the tweet that leads to a version of #ReactQuery based on #XState? Pls say yes.
— Jim Bolla (@jimbolla) February 24, 2022
Rumor has it David and the Stately.ai team are working on a plugin system for XState.
But look what happens to our rendering logic:
switch (state.value) {
case "loading":
return <Spinner />
case "not_found":
return <NotFound />
case "error":
return <Ooopsies />
case "show_less":
return (
<>
<List data={data} count={10} />
<Button onClick={showMore} />
</>
)
case "show_more":
return (
<>
<List data={data} count={data.length} />
<Button onClick={showLess} />
</>
)
default:
return <Spinner />
}
Feels much more readable to me. You see all the states and what they render. Add TypeScript and you'll know at a glance if anything is missing.
And we fixed the bug 😉
Statelyai's VSCode extension 😍
Where state machines shine is visualizing your logic. Reading the original soup of conditionals is hard because you have to picture what's going on in your brain.
That's where the "OMG Don't distract me!" meme about programmers comes in. Holding glass palaces of complexity in your brain is hard. Let the computer do it!
Install the xstate-vscode extension and you get this 👇
Open the inspector and look at this. Right in your editor!
You can click around to simulate events and see what happens. There's a lot of dead ends in our state machine. Users could get stuck 🤔
Editing state machines visually 🤯
The UX of our example component is terrible. All those dead-end states means users can get stuck. Why is there no retry on error? What about not found? What if you want fresh data after reading for a while?
xstate-vscode comes with an editor! It's a little buggy right now and doesn't support everything XState can do, but it's wonderful for fixing issues like this.
That looks messy I agree. The visualizer is wonderful though.
Next time you're dealing with complex UI logic, consider employing a state machine. With Statelyai's new XState VSCode extension, they even shine on a team.
Cheers,
~Swizec
PS: the bug in my first component is that <List />
renders twice when showMore = true
. Users will see the first 10 elements twice
PPS: for an example of refactoring useReducer to XState, try this series of articles
Continue reading about A new VSCode extension makes state machines shine on a team
Semantically similar articles hand-picked by GPT-4
- When your brain is breaking, try Stately.ai
- Reader question: useReducer or XState?
- When your brain is breaking, try XState
- Refactoring a useReducer to XState, pt1 – CodeWithSwiz 11
- Swap useReducer with XState – CodeWithSwiz 13
Learned something new? Want to become a React expert?
Learning from tutorials is easy. You follow some steps, learn a smol lesson, and feel like you got dis 💪
Then comes the interview, a real world problem, or a question from the boss. Your mind goes blank. Shit, how does this work again ...
Happens to everyone. Building is harder than recognizing and the real world is a mess! Nothing fits in neat little boxes you learned about in tutorials.
That's where my emails come in – lessons from experience. Building production software :)
Leave your email and get the React email Series - a series of curated essays on building with React. Borne from experience, seasoned with over 12+ years of hands-on practice building the engineering side of growing SaaS companies.
Get Curated React Essays
Get a series of curated essays on React. Lessons and insights from building software for production. No bullshit.
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️