useStateMachine
A lightweight react hook to create and work with state machines
Read the documentation for more information
Install
Import
yarn add @phenax/use-state-machine
Examples
Manual traffic lights
import useStateMachine from '~/hooks/useStateMachine';
const stateChart = {
id: 'traffixLight',
initial: 'green',
states: {
green: { on: { NEXT: 'red' } },
orange: { on: { NEXT: 'green' } },
red: { on: { NEXT: 'orange' } },
},
};
export default function ManualTrafficLights() {
const { cata, state, dispatch } = useStateMachine(stateChart);
return (
<Fragment>
<div
className="trafficLight"
style={{
backgroundColor: cata({
green: '#51e980',
red: '#e74c3c',
orange: '#ffa500',
}),
}}
>
The light is {state}
</div>
<button onClick={() => dispatch('NEXT')}>Next light</button>
</Fragment>
);
};
Automated traffic lights with onEntry action
onEntry
is called every time you enter a given state. onEntry
is called with the current state machine instance.
import useStateMachine from '~/hooks/useStateMachine';
const stateChart = {
id: "traffixLight",
initial: "green",
states: {
green: {
onEntry: waitForNextLight,
on: {
NEXT: "red"
}
},
orange: {
onEntry: waitForNextLight,
on: {
NEXT: "green"
}
},
red: {
onEntry: waitForNextLight,
on: {
NEXT: "orange"
}
}
}
};
function waitForNextLight({ dispatch }) {
const timer = setTimeout(() => dispatch('NEXT'), 1000);
return () => clearTimeout(timer);
}
function TrafficLights() {
const { cata, state, dispatch } = useStateMachine(stateChart);
return (
<Fragment>
<div
style={{
width: "30px",
height: "30px",
backgroundColor: cata({
green: "#51e980",
red: "#e74c3c",
orange: "#ffa500"
})
}}
>
The light is {state}
</div>
<button onClick={() => dispatch("NEXT")}>Force next light</button>
</Fragment>
);
}
Fetching data
You can use context to store any data associated with a state.
const stateChart = {
id: 'userData',
initial: 'idle',
context: {
data: null,
error: null,
},
states: {
idle: {
on: {
FETCH: {
target: 'pending',
action: ({ dispatch }, userId) => {
fetchUser(userId)
.then(user => dispatch('SUCCESS', user))
.catch(error => dispatch('FAILURE', error));
},
},
},
},
pending: {
on: {
SUCCESS: {
target: 'success',
beforeStateChange: ({ updateContext }, data) => updateContext(c => ({ ...c, data })),
},
FAILURE: {
target: 'failure',
beforeStateChange: ({ updateContext }, error) => updateContext(c => ({ ...c, error })),
},
},
},
},
};
const UserData = () => {
const { context, dispatch, cata } = useStateMachine(stateChart);
return (
<div>
{cata({
idle: () => (
<button onClick={() => dispatch('FETCH')}>
Fetch user data
</button>
),
pending: () => <Spinner />,
success: () => `Hi ${context.data.name}`,
failure: () => `Error: ${context.error.message}`,
})}
</div>
);
};