final-form-set-field-data
A Final Form mutator that lets you attach and merge arbitrary metadata onto individual form fields.
Repository Health
Technical Analysis
final-form-set-field-data is a small mutator plugin for the Final Form form-state library. It adds a single setFieldData mutator that merges an arbitrary object of metadata into a specific field’s data property, which Final Form already exposes to subscribers via fieldState.data.
This is useful for attaching UI-only concerns to a field — such as whether it should currently be visible, disabled, marked as “just validated”, or tagged with any other app-specific flag — without threading that state through your own separate store or re-deriving it from the field’s value on every render. Because it plugs into Final Form’s existing mutator and subscription system, consuming components only need to subscribe to data to react to changes.
The package is deliberately minimal: it ships one function, has no runtime dependencies beyond the final-form peer dependency, and is maintained under the official final-form GitHub organization alongside the core library and its other companion mutators (e.g. final-form-arrays, final-form-calculate).
What You Get
- A single
setFieldData(name, data)mutator you register withcreateForm({ mutators: { setFieldData } }) - Shallow-merge semantics — calling it repeatedly merges new keys into the field’s existing
dataobject rather than replacing it - No-op safety — calling it for a field name that doesn’t exist in form state does nothing instead of throwing
- Direct integration with Final Form’s existing field subscription model via
fieldState.data - A tiny, dependency-free implementation (only
final-formas a peer dependency) with both CommonJS and ES module builds
Common Use Cases
- Marking a field as “just validated” or “touched via autofill” to drive a transient UI highlight
- Storing per-field UI flags such as
visible,disabled, orcollapsedthat are computed outside the normal value/validation flow - Tagging a field with metadata from an async lookup (e.g. an address-autocomplete suggestion source) for the rendering component to read
- Coordinating custom field-level UI state across sibling components that all subscribe to the same field’s
data
Under The Hood
Architecture
The entire package is two files — src/index.js re-exporting src/setFieldData.js — with no internal layering, since the whole surface area is a single Final Form mutator matching the library’s Mutator type signature (args, state, tools) => void. setFieldData destructures [name, data] from args, looks up state.fields[name] directly on Final Form’s own mutable internal state object, and shallow-merges into field.data ({ ...field.data, ...data }), silently no-op’ing if the field isn’t registered. There’s no dependency injection, no internal composition, and no data flow beyond this one side-effecting mutation against the host library’s state tree — the package’s only job is to slot into Final Form’s existing mutator contract, so there is nothing that would “break” beyond needing to track whatever MutableState/Mutator shape a future Final Form major version exposes.
Tech Stack
Written in Flow-typed JavaScript (// @flow pragmas plus a hand-written index.js.flow declaration) and built with Rollup (rollup.config.js) into CommonJS, ES module, and UMD bundles via rollup-plugin-babel and Babel’s @babel/preset-env/@babel/preset-flow. Scripts are orchestrated through nps/nps-utils rather than raw npm scripts, bundle size is enforced with bundlesize against the built UMD/ES/CJS artifacts, and the only runtime relationship is a final-form peer dependency (no runtime dependencies of its own). CI runs on Travis (.travis.yml), with husky + lint-staged running Prettier and ESLint on commit.
Code Quality
A single Jest test file (src/setFieldData.test.js) exercises the mutator directly: it verifies the no-tool-calls contract (Final Form passes getIn/setIn/changeValue/shallowEqual helpers the mutator never touches), correct merge behavior, preservation of existing sibling fields, and the no-op path for an unregistered field name — solid coverage given the function’s small surface area, though it’s the only source file under test since it’s also the only source file. Typing is via Flow rather than TypeScript, and ESLint (eslint-config-react-app) plus Prettier are wired into precommit via lint-staged, but there is no evidence of a CI-enforced type-check step beyond what Travis runs.
What Makes It Unique
There is no novel technical approach here — it’s a minimal, intentionally boring adapter that takes advantage of a capability Final Form’s mutator/fieldState.data system already exposes. Its value is entirely in scope discipline: it does one merge operation and nothing else, matching the pattern of the other official final-form-* companion mutators (final-form-arrays, final-form-calculate) rather than introducing new architecture or techniques.