An Interest In:
Web News this Week
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
- April 12, 2024
React State Machine Hook
This custom hook is meant to live somewhere in between the built-in useReducer
and pulling in a 3rd party library like xstate.
let { state, status } = useStateMachine( stateChart, initialState, updaters, transitionEffects?);
It's not quite useful/big enough to warrant an NPM package, so I created a code snippet and will document it here for the next time I reach for it.
1. Document the State and available Statuses
The State Machine will track 2 things,
status
- the State Machine's state, calledstatus
to avoid confusing with Reactstate
.state
- The stateful data that should be tracked in addition tostatus
. This is just like the state foruseReducer
.
export interface AuthState { error: string; currentUser: { uid: string; name: string; email: string };}const initialState: AuthState = { currentUser: null, error: ""};export type AuthStatus = | "UNKNOWN" | "ANONYMOUS" | "AUTHENTICATING" | "AUTHENTICATED" | "ERRORED";
2. Create the State Chart
For each status, what actions can be performed? If that action runs, to which status should it transition?
The names of the actions should match the names of the updaters in the next step.
const stateChart: StateChart<AuthStatus, typeof updaters> = { initial: "UNKNOWN", states: { UNKNOWN: { setCachedUser: "AUTHENTICATED", logout: "ANONYMOUS", handleError: "ERRORED" }, ANONYMOUS: { loginStart: "AUTHENTICATING" }, AUTHENTICATING: { loginSuccess: "AUTHENTICATED", handleError: "ERRORED" }, AUTHENTICATED: { logout: "ANONYMOUS" }, ERRORED: { loginStart: "AUTHENTICATING" } }};
3. Implement the State Updaters
A state updater is a function that takes in the current state (a React state) and the triggered action, then returns the updated state. Just like a reducer.
(state, action) => updatedState
- Some actions are triggered just to cause a State Machine
status
transition, likeloginStart
. In this case, just return thestate
right back. - The names of the updaters should match the names of the actions in the State Chart.
const updaters = { loginSuccess: (state, { user }) => { cacheCurrentUser(user); return { error: "", currentUser: user }; }, setCachedUser: (state, { user }) => { return { error: "", currentUser: user }; }, logout: (state) => { cacheCurrentUser(null); return { error: "", currentUser: null }; }, handleError: (state, { error }) => { return { ...state, error: error.message }; }, loginStart: (state, { username, password }) => state};
4. Use and Define Transition Effects
The last step is to use the hook.
You can also define effect functions to be run when the state machine transitions into a specified state. This is useful for doing async work.
The enter
transition is given the action
that caused the transition as well as all the available actions
.
In this example, when the user calls, loginStart
, the status will transition to AUTHENTICATING
, which will trigger the transition to call api.login
. Based on the result of login()
, either the success or error action is triggered.
function useAuth() { let stateMachine = useStateMachine(stateChart, initialState, updaters, { AUTHENTICATING: { enter: async ({ action, actions }) => { try { let user = await api.login({ username: action.username, password: action.password }); actions.loginSuccess({ user }); } catch (error) { actions.handleError({ error }); } } }, UNKNOWN: { enter: () => { let cachedUser = getCurrentUserFromCache(); if (cachedUser && cachedUser.token) { stateMachine.actions.setCachedUser({ user: cachedUser }); } else { stateMachine.actions.logout(); } } } }); return stateMachine;}
Original Link: https://dev.to/droopytersen/state-machine-hook-hdg
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To