diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df1856d..441d38b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v3.5.3 - 2026-06-25 + +### Fixed + +- **Render-phase state updates left the DOM inconsistent with the render.** When a + component updated its own state during render (`if x { setState(...) }` — the + derived-state-during-render pattern), the setter stored the new value and + scheduled a commit, but the component was never re-run, so the committed DOM + showed a value the component had not actually rendered (e.g. it rendered `0` but + the DOM showed `1`, and it never converged). `renderFunctionComponent` now + detects a render-phase update (the setter's owner is the fiber actively + rendering, tracked by a new `activeRenderFiber` flag — distinct from the ambient + current fiber so it never trips on the SSR path or hook unit tests) and re-runs + the component to convergence, bounded at 25 iterations with a diagnostic to + guard against an unconditional-setState infinite loop. Matches React's + "keeps restarting until there are no more new updates." Covered by + `TestRenderPhaseUpdateConverges` / `TestRenderPhaseDerivedStateOneStep` / + `TestRenderPhaseUnconditionalDoesNotHang`. + ## v3.5.2 - 2026-06-25 ### Fixed diff --git a/examples/public-examples-site/assets/docs/latest-release-notes.md b/examples/public-examples-site/assets/docs/latest-release-notes.md index d8eaa8c7..0ef4a482 100644 --- a/examples/public-examples-site/assets/docs/latest-release-notes.md +++ b/examples/public-examples-site/assets/docs/latest-release-notes.md @@ -3,18 +3,18 @@ This docs-site page mirrors the first section returned by `tools/changelogcheck.LatestEntry(CHANGELOG.md)`. -## v3.5.2 - 2026-06-25 +## v3.5.3 - 2026-06-25 -The current latest changelog section is `v3.5.2 - 2026-06-25`. **Fixed:** -`defaultValue`/`defaultChecked` rendered as inert, browser-ignored attributes in -SSR, so an uncontrolled form field set via `defaultValue` rendered empty -server-side. The SSR serializer (buffered + streaming) now maps `defaultValue` → -the element's controlled value and `defaultChecked` → `checked` (unless the -controlled prop is already set, so an explicit `value` wins). Because it runs -before the controlled-value logic, it flows through every form element: `` -gets a `value`, `