commit 3fdbe55a11f5512c971c52a207c595daa2d34ccc
parent 8b68b4759af6fd9bb0adfdc20ee14385f213aaa1
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 1 Oct 2026 03:05:34 -0400
plans: slice FK review — L1's bullet, I1 (the digest boxes give way to another tab's save), I2 (a controlled select resets to its server-rendered or first option), I3 (a blank select for a value no option has)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/plans/FACTS.md b/plans/FACTS.md
@@ -8051,8 +8051,10 @@ source mirror (homepage)". Anchors are at the branch.
mount is applied only when `multiple` toggles (`:22447`);
- a **controlled checkbox, radio or select does not survive**: `defaultChecked` is set only at
mount (`initInput`, `:1849`; `updateInput` writes `checked` alone, `:1797-1798`), and a controlled
- select's options get `defaultSelected` only at mount (`:22444`). After any submit the DOM shows
- what it mounted with while state holds the choice, **until the next re-render that reaches the
+ select's options NEVER get `defaultSelected` (`updateOptions(…, false)` at mount, `:22121`, and on
+ update, `:22444`), so the reset selects the server-rendered `selected` option, or the FIRST
+ option for a select mounted on the client (a membership's group select that appears on a tick).
+ After any submit the DOM shows that while state holds the choice, **until the next re-render that reaches the
element** re-asserts the prop (React 19 calls `updateInput` with every prop on any update) — in
the editor 0.3 to 7 s, the auto-refresh being the usual one; never, with it off. A NAMED one
submitted in that window posts the stale choice.
diff --git a/plans/release-16.md b/plans/release-16.md
@@ -574,7 +574,7 @@ refusal showed:
| Uncontrolled text input, textarea, checkbox | back to the stored value, for good | the reset; no action returned what was typed (the inventory's finding) |
| Uncontrolled `<select>` | back to the option it MOUNTED with, **after a success too**: `cookieMode` saved as `defer` still read `when-required` 7 s later, and a second save would send that | a `defaultValue` change after mount is never applied |
| A focused number input (Enter submits) | back to its old default | `setDefaultValue` skips a focused number input |
-| Controlled checkbox, radio, select | back to what it mounted with **until the next re-render reaches it**: the workers' Enabled boxes between 0.3 and 1.5 s; a site's accent and membership, the sync form's sweep select, a new channel's platform and handling between 1.5 and 7 s (the auto-refresh, 5 s by default; never, with it off). A named one posts the stale choice if submitted in that window | `defaultChecked` / `defaultSelected` are set only at mount |
+| Controlled checkbox, radio, select | back to what it mounted with (a select: its server-rendered option, or its first when it mounted on the client) **until the next re-render reaches it**: the workers' Enabled boxes between 0.3 and 1.5 s; a site's accent and membership, the sync form's sweep select, a new channel's platform and handling between 1.5 and 7 s (the auto-refresh, 5 s by default; never, with it off). A named one posts the stale choice if submitted in that window | `defaultChecked` is set only at mount; a controlled select's options never get `defaultSelected` |
| Controlled text input | kept | React keeps its `defaultValue` in step with `value` |
**What it does.**
@@ -673,11 +673,20 @@ the page mounted with (the uncontrolled select for good). The `[Unreleased]` bul
- **An action that throws past its own `try`** (`createChannel` itself, for one) still reaches the
form as Next's error, not as `{ error, values }`. Every caught failure carries `values`;
converting the uncaught ones is a behaviour change of its own.
-- **A field not rendered at the submit** is seeded as not posted: a checkbox reads unticked when the
- next render shows it (ChannelForm create, switching a URL between a social and a video source after
- a refusal: `fetchPlaylist` comes back unticked). A text field keeps its initial value.
+- **A field not rendered at the submit** is seeded as not posted: a checkbox reads unticked when a
+ later render shows it, a text field keeps its initial value. The one place a form switches what it
+ renders is ChannelForm create (a social or a video source), and its **On create** boxes seed only
+ from a refused submit of their own kind (review L1, below); every other box a social submit leaves
+ out starts unticked in create mode anyway.
- **A seeded select or number input re-mounts when its stored value changes under it** (a save from
another tab), and an in-progress edit of THAT field is lost; every other field keeps its edit.
+ DigestSettingsForm's section boxes do the same: they re-read the stored list whenever it changes
+ and the last result was not a refusal, before any submit too, so unsaved ticks give way to another
+ tab's save (on `main` a touched box kept its tick).
+- **`ControlledSelect` puts back a value no option has as a blank select** (`selectedIndex` −1);
+ React's own update selects the first enabled option instead. Reachable for `WorkersField`'s engine
+ select (`row.appId ?? ""`, no `""` option) and a membership whose group was deleted. Visual only:
+ both post from state.
- **The other way to do it**, for the record and not tried: `onReset={(e) => e.preventDefault()}` on
each action form should cancel React's reset outright, so nothing is wiped and nothing needs seeding, but the
success path would keep what was typed instead of re-reading the stored values. The ruling chose