import { Meta, Canvas } from '@storybook/addon-docs/blocks'; import * as AsyncStories from '../app/shared/ui/async/async.stories'; # Functional programming in the UI The components in this library are the _view_. Behind them, three small functional tools do the heavy lifting — all so that **illegal states can't be represented**. This page is the Storybook front door; the full narrative lives in `docs/fp-tea-atomic-design.md`, and a side-by-side "before/after" runs at the app's **`/concepts`** route. ## 1. `RemoteData` — async has four states, not a boolean soup `src/app/shared/application/remote-data.ts`. Instead of juggling `loading`, `error`, and `data` flags (which permit "loading **and** error" nonsense), one tagged union: `Loading | Empty | Failure | Success`. You combine sources with `map`/`map2`/`andThen` and render it through the `async` molecule — exactly one of four templates shows, by construction: ## 2. The Elm-style store — all state in one Model, changed only by pure `reduce` `src/app/shared/application/store.ts` + the `*.machine.ts` files. State is one tagged-union value; the template never mutates it, it `dispatch`es a message and a **pure** `reduce(model, msg)` returns the next state. Side effects live in a _command_, never in the reducer: ```ts // reducer = "what the new state is" — pure, testable, no I/O function reduce(model: Model, msg: Msg): Model { … } // command = "go do it, then say what happened" async function submit(...) { const res = await http(...); dispatch(res.ok ? { tag: 'Submitted' } : { tag: 'Failed', error: res.error }); } ``` Because state is one value, the whole thing is inspectable and every transition has a spec. ## 3. Parse, don't validate — raw input becomes a branded type once `src/app/registratie/domain/value-objects/`. A `Postcode` is a distinct type from `string`, mintable only through `parsePostcode`, which returns a `Result`. Once you hold the type, you never re-check it — the type _is_ the proof. Compose the parse pipeline with the `Result` combinators in `src/app/shared/kernel/fp.ts` (`map`, `mapErr`, `andThen`, `fold`) rather than hand-branching `r.ok ? … : …` at every step. ```ts parsePostcode(raw) // Result |> mapErr(toLocalizedMessage) // swap raw msg → UI copy |> map(toDomain) // only runs on success ``` ## How it connects to atomic design Atoms and molecules are pure view functions of their inputs; pages are the TEA runtime (the "shell") that holds the store and wires effects. Same inward-pointing discipline as the [layer rule](?path=/docs/foundations-atomic-design--docs), applied to state and effects instead of imports.