Forms
Represent each field with its own signal, editable value, or draft. A record of reactive fields lets each edit update only the readers of that field.
A form as a record of signals
A record of reactive fields lets each edit update the readers of that field alone.
A two-field person form
Both fields use drafts so that upstream reloads preserve local edits. The helpers collect a snapshot, reset both fields in a batch, and report whether either has been edited.
type Person = { Name: string; Age: int }
type PersonForm =
{
Name: Editable<string>
Age: Editable<int>
}
module PersonForm =
let create (person: Signal<Person>) =
{
Name = createDraft (fun _ -> person.Value.Name)
Age = createDraft (fun _ -> person.Value.Age)
}
let snapshot (form: PersonForm) : Person =
{ Name = form.Name.Value; Age = form.Age.Value }
let reset (form: PersonForm) =
batch (fun () ->
form.Name.Reset ()
form.Age.Reset ())
let isDirty (form: PersonForm) =
form.Name.IsEdited || form.Age.IsEdited
- A field is a
Signal<'T>when the form owns the value, and anEditable<'T>when it comes from upstream. snapshotand a whole-record set are written by hand once per type. Put a multi-field set in abatch, so an effect that reads several fields runs once.isDirtyis tracked: a memo or an effect over it wakes when any field's edit starts or ends.
Compare with a whole-record signal
Holding the whole record in one signal and reading fields through selectors costs a record copy and a selector run per field on each write. See Migrating from Elmish.