Back

State Management

A number of boxes connect by lines with arrows.

What is the best approach to UI state management.

Last TendedSep 2021
PlantedMay 2021
Statussprout
TagsFundamentals, XState
A rectangle with 3 small squares in it.

Named States

A common approach in React is to use boolean flags to keep track of state. For example, imagine we made a component that fetched an image. It could look like the following:

MyImageFetcher.tsx

const MyImageFetcher = () => {
const [isFetching, setFetching] = useState(false);
const [isSuccess, setSuccess] = useState(false);
const [isError, setError] = useState(false);
...

A disadvantage of this is it results in untended states. The intended amount of states for the component is 4; idle, fetching, success & error. The actual number of states produced from the above approach is 23 = 8. Changing the approach from boolean flags to named states can fix this:

MyImageFetcher.tsx

type State = 'idle' | 'fetching' | 'success' | 'error'
const MyImageFetcher = () => {
const [state, setState] = useState('idle');
...

Intended number of states = 4. Actual number of states = 4.

A square and triangle seperated by a dotted line.

Event-driven vs. State-driven

From a high level, I have seen 2 different approaches to UI state development. Event-driven & state-driven. Event-driven development centers development around events. The event comes 1st & the state the system is in comes 2nd. In my experience, this commonly occurs when using RxJS or Redux. State-driven development centers around state. Development begins with state, followed by events. This approach is encouraged when using libraries such as XState. In my experience, state-driven development is a significantly better fit for UI development. More information on this can be found here.

A big and small cog.

Finite-State Machines

I believe finite-state machines are the best fit approach to UI state management.

Advantages:

  • Seperation of concerns: Logic can be seperated from everything else. This allows logic to be reused when changing frameworks (such as moving from Angular to React), easier seperation of work between team members & easier testing / debugging.
  • Named states: is core to the structure (see above why this is an advantage).
  • Scalability: Statecharts scale well as complexity grows. You can easily manage distributed behavior through the actor model.
  • Visualization: Logic can be visualized as a diagram (known as a statechart), acting as accurate documentation, making it easier to understand a system & acting as a communication language between coder / non-coder stakeholders.
  • Exploration: The process of building a statechart encourages all states to be explored. This can prevent edge cases from being overlooked.
  • Battle proven: 30+ years of refinement.
  • Formalised logic: increasing a codebase's consistency.

Disadvantages:

  • Steep learning curve: State machines are a significantly different way of thinking about state when compared to popular state management libraries such as Redux.
  • Over-engineering: A state machine may seem like going overboard when a small amount of state management is required, such as a toggle that only requires a "on" & "off" state. This can be achieved with a small amount of code using React's "useState" hook. The counter-argument to this is, often what begins as simple state grows in complexity. State machines may be more work to begin with but less work at scale.

Currently, XState is the most popular JavaScript finite-state machine library.

Source
A computer screen with a blinking cursor.

Examples

4 rectangles upright, the last on a slight angle.

Courses