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:
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:
Intended number of states = 4. Actual number of states = 4.
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.
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.