Left Arrow.Back
Left Arrow.Back

Notes / JavaScript / State Management

State Management

A number of boxes connect by lines with arrows.

State Management

Last Tended

Status: sprout

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:


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:


type State = 'IDLE' | 'FETCHING' | 'SUCCESS' | 'ERROR'
const MyImageFetcher = () => {
const [state, setState] = useState('IDLE');

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

2 characters in an arcade game about to fight.

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.

2 cogs rotating

Finite-State Machines

Finite-state machines are the best fit approach to UI state management.


  • Disable all user actions by default: Within each state, you have a centralized area where you define what events can occur, reducing edge cases.
  • 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.


  • 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.