From 5f66c8129c7cd2b65de16f00b40468c0672d5386 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 18 Aug 2026 15:33:36 +0530 Subject: [PATCH] docs(react-islands): drop the write-during-render guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented and removed. A render-phase flag cleared on a microtask is still set when React runs effects, so islands writing from an effect — the documented correct pattern — would throw. The flag also cannot be set for an island's own re-renders, so real violations pass silently. Detecting React's render phase reliably needs React internals, which is not acceptable in a shipped framework. React already reports the real hazard, and the getSnapshot caching requirement covers the loop case. Co-Authored-By: Claude Opus 5 --- .../specs/2026-08-18-react-islands-design.md | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-18-react-islands-design.md b/docs/superpowers/specs/2026-08-18-react-islands-design.md index 8d2777ea..6d81a1f2 100644 --- a/docs/superpowers/specs/2026-08-18-react-islands-design.md +++ b/docs/superpowers/specs/2026-08-18-react-islands-design.md @@ -127,9 +127,9 @@ record that subscribers and devtools depend on. Write → action → store notifies → snapshot changes → island re-renders. This terminates cleanly **provided writes never occur during render**. Writes belong in event handlers or effects. -This rule is enforced in dev (see Error handling), not merely documented. It is also the reason -this shape was chosen over generated `bind:` sugar: the cycle stays visible in the author's own -code rather than being hidden in generated glue. +This rule is documented rather than machine-enforced — see "Dropped: the write-during-render +guard" below. It is also the reason this shape was chosen over generated `bind:` sugar: the +cycle stays visible in the author's own code rather than being hidden in generated glue. ## Compiler and bundler changes @@ -189,12 +189,38 @@ point. | Island bundle fails to load | Placeholder remains, warning logged; page stays functional because everything else was server-rendered | | Non-serializable props | Compile-time `WRN-ISLAND-PROPS` | | Unknown store name | Dev: throw, listing available store names. Prod: warn, return undefined | -| Action fired during render | Dev: throw with a targeted message pointing at the handler/effect rule (React's own warning is too generic to diagnose quickly) | +| Action fired during render | Left to React. See "Dropped: the write-during-render guard" below. | | Cleanup throws on unmount | Caught and logged; navigation must not break | Islands failing **locally** is the most valuable property of this model: a crashed chart leaves the rest of the page working. +## Dropped: the write-during-render guard + +The design originally called for a dev-only guard that threw when an island +called a store action during render. It was implemented, then removed: the +mechanism is unreliable in both directions. + +- **False positives.** React runs effects before a queued microtask drains, so a + render-phase flag cleared on a microtask is still set inside `useEffect`. An + island writing from an effect — the documented correct pattern — would throw. +- **False negatives.** The flag can only be set from the error boundary`s + render. When an island updates its own state, only the island re-renders, so + the flag is never set and a genuine write-during-render passes silently. + +There is no reliable public API for detecting React`s render phase; doing it +properly requires React internals, which is not acceptable in a shipped +framework. + +React already covers the real hazard: writing during render that notifies +subscribers produces "Cannot update a component while rendering a different +component", and the infinite-loop case is caught by the `getSnapshot` caching +requirement handled in the store bridge. The custom guard added false positives +without covering anything React misses. + +The author-facing rule still stands and is still documented — it is simply not +machine-enforced. + ## Testing ### Compiler unit tests