15.7 · Effects and cancellation, modelling state, composing reducers
The first pass explained why one-way state is predictable. In practice the hard parts are everything around the pure function: effects that finish out of order, states that should not exist, features that must compose, and time.
Mental model. A reducer is only as trustworthy as the effects it launches and the states it allows. Make illegal states unrepresentable, make stale results harmless, and make time a dependency.
Effects and state shape
Out-of-order responses, cancellation, and enums instead of flags.
- In a reducer-style store, how do you stop a slow, stale response overwriting a newer one: cancellation, tokens and what a cooperative service does?
An effect is asynchronous, so responses can arrive out of order: the user types “ab”, then “abc”; the “ab” request is slower and lands last, and the screen… - How do you model screen state so illegal combinations cannot exist: enum with payloads versus a bag of flags?
A screen modelled as isLoading: Bool, items: [Item]? and error: String? has 2 × 2 × 2 = 8 combinations, and several are nonsense: loading and failed at once…
Composition
Child reducers inside parents.
- How do you compose reducers: child features inside a parent, scoping state with key paths and embedding actions?
A real app is a tree of features, and each should be written and tested alone. Composition has two parts. Scoping state: a child reducer works on its own small…
More in Architecture & API design
- 15.1 · MVC, MVVM, MVP, VIPER, RIBs, TCA and Clean, and unidirectional data flow
- 15.2 · Coordinators, routers and dependency injection
- 15.3 · Feature modules and boundaries, and strangling a legacy codebase
- 15.4 · throws, Result, typed throws and mapping errors at boundaries
- 15.5 · Designing a Swift API: naming, ownership, documentation and evolution
- 15.6 · SOLID without dogma, and building a platform for many apps
- 15.8 · Closure-based clients, dependency lifetimes, generic repositories and building the graph lazily
- 15.9 · Binary breaks from innocent changes, and what an import exposes
- 15.10 · Types that prevent misuse, error types for libraries, one-shot values and when to trap