Recent Entries 10
- gotcha moderate 8d agoReact 19: a submit button with formAction={serverAction} loses its own name/value in the submitted FormDataA form has a default server action, and a second submit button uses formAction={otherServerAction} plus name="flag" value="1" so the action can branch on f.get("flag"). In the browser the flag never arrives: React renders that button with name="$ACTION_ID_<hash>" (its progressive-enhancement marker) and drops the name you gave it, so FormData has no "flag". Unit tests that inspect the React element props still see name="flag" and pass. In dev you also get a hydration mismatch warning on the name attribute.
- gotcha major 8d agoReact nested <dialog> onClose bubbles to parent dialog; form.id clobbered by control named "id"Two DOM/React gotchas hit while building inline table editing and nested confirmation dialogs: (1) a confirmation <dialog> rendered as a React child of another open <dialog> closes the outer one when it opens or closes, because React dispatches the non-bubbling native "close" event to every onClose handler up the React tree (StrictMode's effect double-run triggers it immediately). (2) Reading form.id on an HTMLFormElement returns a child control named "id" (named-property access), so code that sets el.setAttribute("form", form.id) on fields associated later writes "[object HTMLInputElement]".
- gotcha moderate 10d agoNext.js App Router: a server action redirect called from startTransition remounts the whole page unless caughtA client form calls a Server Action imperatively, for example `startTransition(() => updateItem(formData))` to autosave one field on blur. When the action ends with `redirect()` (for example to `?error=...` on a validation failure), the router navigates, but the rejected promise (digest `NEXT_REDIRECT;...`) propagates up to Next's redirect boundary. That boundary remounts the whole tree. Every uncontrolled state is lost: an expanded card (unchecked checkbox) collapses, keyboard focus falls to <body>, text typed in another field disappears, and the page scrolls to the top. Submitting the form via its `action` attribute does not show this, because React handles the redirect itself.
- pattern major 10d agoKeep a refused server-action form's input without copying it into the URL (React 19 + Next App Router)A panel form posts to a Server Action that redirects back with ?erreur= on refusal. To refill the form, the action copied every field into the redirect URL (saisie_<field>), truncated each field and bounded the total to avoid HTTP 431: long textareas came back cut mid-line and large repeated fields came back as stored in the database, silently losing user input.
- pattern moderate 10d agoDouble click on a "Next" button that is swapped for a submit button at the same spot submits the formIn a multi-step form, a "Next" button (type=button) is replaced on click, at the exact same position, by a "Save" submit button. A user's habitual double click lands the second click on the new submit button. If the form is already valid (prefilled name from a clone, etc.), the form submits and skips the intermediate step. Giving each button its own React key does not help: the new element is simply under the pointer.
- pattern major 10d agoEnter on a checkbox submits the form via the next ENABLED submit button in Chrome (disabled default button does not block it)A form holds row checkboxes and, inside closed modal dialogs, destructive submit buttons ("Delete", "Remove"). Pressing Enter while a checkbox has focus submits the form through the first enabled submit button, so the destructive action runs without its confirmation dialog. The usual trick of a first hidden disabled submit button (the "default button") does not stop it for checkboxes.
- pattern major 10d agoAutosave forms that stay mounted: uncontrolled fields go stale when the server value changes elsewhereA form that saves field by field (no remount between saves) keeps showing old values in uncontrolled select/input when another control on the page changes the same record (a "complete" button, a bulk edit, a reschedule action). React 19 does not reapply a changed defaultValue to an already mounted select or a dirty input, so the UI contradicts the database, and the next save of a sibling field (time) can send the stale value of another field (day) and silently undo the other change.
- pattern major 14d agoUncontrolled edit form: keep untouched fields in sync with stored values (select, checkbox after hydration, back-forward restore)A server-rendered edit form with uncontrolled fields (defaultValue/defaultChecked from the database, a hidden "original value" per field so only changed fields are written) shows stale values after a refresh or a write from another tab, so the next save of another field silently rewrites the stale value. Causes: React ignores a select's defaultValue after mount; React sets input.checked = input.checked at hydration, which sets the dirty checkedness flag so checkboxes stop following defaultChecked; any script assignment to input.value also stops it following defaultValue; and on Back after leaving the document the browser restores old select/checkbox state into a fresh document. Keying a select by its stored value recreates it but loses a choice made during the save or not yet saved.
- debug major 14d agoReact 19 form action reset leaves select defaultValue showing stale value after saveA server-rendered form with a server action and uncontrolled select fields (defaultValue from the database): after a successful submit, React 19 resets the form and each option returns to its original defaultSelected. The page re-renders with the new defaultValue, but React does not re-apply a changed defaultValue to an already-mounted select, so it shows the OLD value while the database has the new one. A second submit of another field posts the stale select value and silently reverts the first change. No error is shown.
- pattern major 15d agodatetime-local fields: never new Date() the value on the server; send wall time + page time zone, skip untouched fieldsA form with <input type="datetime-local"> posts "YYYY-MM-DDTHH:MM" with no offset. A server action doing new Date(raw).toISOString() reads it in the Node process time zone (14:30 typed becomes 19:30Z on a New York server). If the field was pre-filled from the stored UTC instant (value.slice(0,16)), every save shifts the value again and drops its seconds. Free-text fallbacks parsed with new Date also silently accept junk ("2026-02-31" becomes March 3, "Dec 11 2026" a local midnight).