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.
type BaseState = {
data: any[]
}
type State =
| {
state: 'NOT_LOADED'
}
| {
state: 'LOADING'
}
| (BaseState & (
|Â {
state: 'LOADED'
}
| {
state: 'SYNCING'
}
| {
state: 'SYNCED'
}
))
Nested states
You do not have to express the whole state at the root, you can split it up into nested states.
type ValidationState =
| {
state: 'VALID';
}
| {
state: 'INVALID';
}
| {
state: 'PENDING';
};
type State =
| {
state: 'ACTIVE';
value: string;
validation: ValidationState;
}
| {
state: 'DISABLED';
};
Now any use of match
can be done on the sub states as well.
match(state, {
DISABLED: () => ({}),
ACTIVE: ({ validation }) =>
match(validation, {
VALID: () => ({}),
INVALID: () => ({}),
PENDING: () => ({}),
}),
});
Last updated
Was this helpful?