Introduction
State management in a developer's journey can be hell and bliss, and this depends on the library one chose and how well he grasps the logic behind it right? I must say using a state manager never got better when you can visualize your code logic and decide the kind of beans you're cooking.
In today's session, I'll show us how spectacularly the X-state machine works in practice, and how generously it can make your experience in development worthwhile.
What is a Finite State Machine
The finite state machine can be seen as a mathematical “model” that performs a certain computational activity that assumes an initial state and spins through different[specified] states and back to the normal [accepting state]...
More explanation can be found in my previous article "The finite state Machine"
What is X-State
Unlike redux, recoil, Zustand, and a million others, X-State is a library that helps you create finite states, have a visual interpretation, and also execute the logic seamlessly where for instance you will not have to wrap your whole app in a provider, store something or dispatch anything. It's just easy to use and maintain. you can find more explanations here
Getting Started
In this guide we will start by learning how to make a simple dark/light mode, and furtherly a traffic light for our react application all from scratch.
You can find all the working code here on my GitHub.[drop me a star thanks!]
Setting up
We need to create our app to run this so let's run this;
yarn create react-app react-xstate-finite-machine
or
npx create react-app react-xstate-finite-machine
Moving forward, we add the xstate
package to the app;
yarn add xstate @xstate/react
Let's run our app yarn start
or npm start
[if you are using npm to set up], while our app is building.
Create a file with the name you wish and that is where we will initialize the state like so;
import { Machine } from "xstate";
export const switchThemes = Machine({
id: "themes",
initial: "light",
states: {
light: {
on: { SWITCH: "dark" },
},
dark: {
on: { SWITCH: "light" },
},
},
});
Break down
id: just like any state manager it is the name of the name assigned to the state.
initial: that's the [initial]assignment name to the state we want to work with.
states: this is the area where the state resides.[light&dark]
switch & on: that is what we use to fire the action of switching the themes.
ShowTime
Now it's time to visualize the beans we have been cooking, and x-state
provides us with a tool called the visualizer which will show us how our state will logically work.
Now you can see that the logic circle began from the initial state of light
and when a switch
is invoked it'll change back to dark
. Nicely yhh?
Back to our code, we would like to see our state in action, so we call our state component to App.js
, also in order for the state to function fully we have to call the useMachine
hook from the x-state
library and we should have something like this.
import "./App.css";
import { useMachine } from "@xstate/react";
import { switchThemes } from "./states/darkLightSwitch";
function App() {
const [current, send] = useMachine(switchThemes);
const handleSwitch = () => {
send("SWITCH");
};
console.log(current.state);
return (
<div
className={
current.matches("light") ? "light-theme body" : "dark-theme body"
}
>
<button onClick={handleSwitch}>
{current.matches("light") ? "light" : "dark"}
</button>
<h1>{current.matches("light") ? "Light Turnels" : "Dark Moments"}</h1>
</div>
);
}
export default App;
NB the light-theme
& dark-theme
are my CSS classes and they are in their separate folder.
Exciting, isn't it? Now let's get even deeper.
Traffic Lights
Having learned how to add themes to your app why don't we go deeper yeah?
Our traffic lights will assume this looks for now;
We will add a traffic light system into the app and follow prior setup guides for the dark & light
the theme, we will be having our trafficking[excuse my french] light state like so;
import { createMachine } from "xstate";
export const trafficMachine = createMachine({
id: "trafficLights",
initial: "stop",
states: {
stop: {
on: {
NEXT: {
target: "ready",
},
},
},
ready: {
on: {
NEXT: {
target: "go",
},
},
},
go: {
on: {
NEXT: {
target: "stop",
},
},
},
},
});
Yet again visualizer to confirm our act;
Moving forward we will call the state in our application and this time around we will be using our darling useEffect
to set our timer for the traffic light action, and the entire code should look like this;
import "./App.css";
import { useEffect } from "react";
import { useMachine } from "@xstate/react";
import { switchThemes } from "./states/darkLightSwitch";
import { trafficMachine } from "./states/trafficControlState";
function App() {
const [state, send] = useMachine(switchThemes);
const [current, push] = useMachine(trafficMachine);
const handleSwitch = () => {
send("SWITCH");
};
useEffect(() => {
let trigger = () => {
push("NEXT");
};
let timer = setInterval(() => {
trigger();
}, [3000]);
return () => clearInterval(timer);
});
return (
<div
className={
state.matches("light") ? "light-theme body" : "dark-theme body"
}
>
<div className="container">
<button onClick={handleSwitch}>
{state.matches("light") ? "light" : "dark"}
</button>
<h1>{state.matches("light") ? "Light Turnels" : "Dark Moments"}</h1>
</div>
<div>
<ul className="light-container">
<li className={current.matches("stop") && "red"}>
{current.matches("stop") && "STOP"}
</li>
<li className={current.matches("ready") && "yellow"}>
{current.matches("ready") && "READY"}
</li>
<li className={current.matches("go") && "green"}>
{current.matches("go") && "GO"}
</li>
</ul>
</div>
</div>
);
}
export default App;
You will notice the call of useMachine
hook twice, now that's because x-state will not allow you to call more than two actions to the function like our normal useState
wouldn't.
Check out the app in different modes.