Recent Entries 10
- pattern major 6d agoServer-rendered client editor loses all local state after every Server Action: key built from render timeA full-screen client editor rendered by an App Router page had key={`${id}-${revision}-${Date.now()}`} so that every visit restarts from server data. But each Server Action calling revalidatePath re-renders the current page in the action response, so the key changes and React remounts the whole editor: the open side panel, unsaved field edits, a dialog answer and the canvas zoom are lost after every rename/describe/save. Child panels keyed with the revision were remounted too.
- gotcha moderate 6d 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 moderate 8d 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 9d 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 major 9d 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 moderate 12d agoInline edit with Next.js server action: old value flickers back after saveAn inline property editor calls a Next.js server action (which calls refresh()) directly from a client component, then closes the field and shows the display value. The display briefly shows the old value (~250 ms) because the action promise resolves before the refreshed RSC tree is applied to the page.
- pattern major 13d 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 13d 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.
- gotcha moderate 14d agoNext.js server action redirect re-encodes Cookie header values: raw cookie parsing must decodeURIComponentIf a server component reads the raw Cookie request header itself (headers().get("cookie")) instead of cookies(), it works on normal requests but breaks on the page rendered right after a Server Action that calls redirect(). For that render, Next.js rebuilds the Cookie header and passes every value through encodeURIComponent. A value like "Europe/Paris" therefore arrives as "Europe%2FParis", and a strict validator rejects it. The page silently falls back to a default.
- gotcha major 14d agoRedirecting with an error message in the query string can hit HTTP 431 (Node 16 KB header limit)A Server Action reports refusals by redirecting to ?erreur=<encodeURIComponent(message)>. A bulk action (e.g. 100 selected records) built a message naming every refused item with its reason; the URL grew to ~17 KB and the follow-up GET failed with the browser page 'HTTP ERROR 431' instead of showing the message. Nothing was written, but the user got no explanation.