Explicit states

The states of a feature is explicit. That means we have an explicit state property which holds the name of a given state the feature can be in, with any related values.

export type State =
  | {
      state: 'UNAUTHENTICATED'
    }
  | {
      state: 'AUTHENTICATING'
    }
  | {
      state: 'AUTHENTICATED'
      user: { name: string }
    }
  | {
    state: 'ERROR'
    error: string
  }

Thinking in terms of explicit states is a mind shift. You will spend a lot more time identifying what unique states each piece of the flow represents and how to properly name them.

The name of a state should ideally fit with the sentence "I am $STATE". Imagine the feature telling you what state it is in. This can sometimes feel unnatural and a state like ERROR might pop up, which is perfectly fine. The important thing is that it is not imperative, like AUTHENTICATE or SIGN_IN.

The explicit states are expressed and transitioned with a reducer.

import { createReducer, States } from 'react-states'

export type State =
  | {
      state: 'UNAUTHENTICATED'
    }
  | {
      state: 'AUTHENTICATING'
    }
  | {
      state: 'AUTHENTICATED'
      user: { name: string }
    }
  | {
    state: 'ERROR'
    error: string
  }
  
export type PublicAction = {
  type: 'AUTHENTICATE'
}

export type Feature = States<State, PublicAction>
  
const reducer = createReducer<Feature>({
  UNAUTHENTICATED: {},
  AUTHENTICATING: {},
  AUTHENTICATED: {},
  ERROR: {}
})
  
export const FeatureProvider = () => {
  const feature = useReducer(reducer, {
    state: 'UNAUTHENTICATED'
  })
  ...
}

Base state

Some values might occur in multiple states. In this situation you can create a BaseState which is composed with two are more states.

Nested states

You do not have to express the whole state at the root, you can split it up into nested states.

Now any use of match can be done on the sub states as well.

Last updated

Was this helpful?