Dev HMR Server Component Change Before Hydration Causes React State Update On Unmounted Component
Editing a server component before the page finishes hydrating triggers a React warning ('Can't perform a React state update on a component that hasn't mounted yet') and can lead to a hydration mismatch. The issue occurs only in Next.js dev mode with HMR active, both Turbopack and webpack.
The HMR WebSocket receives a `serverComponentChanges` message while the router is still rendering for the first time. The message handler immediately calls `hmrRefresh()`, which dispatches a router action through `useActionQueue` before the Router component has committed. This sets state on a fiber that is not mounted yet, causing React's development warning. If the server component change alters the rendered output, a hydration mismatch follows because the client tries to hydrate against the old HTML.
1. Install dependencies and run `npm run dev`.
2. Open http://localhost:3000 with browser console open. The page displays 'Status: hydrating...' for about 8 seconds, then 'Status: hydrated'.
3. Reload the page. While it still shows 'hydrating...', edit `app/page.js` and change `Server text: v1` to `Server text: v2`, then save.
4. Observe the console warning about state update on unmounted component and, if output changed, a subsequent hydration mismatch error.
The patch adds a mounted guard and an action buffer inside `useActionQueue`. Before the router commits, any dispatched action is queued instead of immediately calling the reducer's dispatch. Once the component mounts (useEffect), the queued actions are flushed in order. This ensures that all router state updates happen after the component is mounted, eliminating React's warning and preventing hydration mismatches from HMR-triggered server changes.
Edge Case Audit
The queued actions are flushed in useEffect, which runs after the first paint. This may cause an additional render immediately after mount; that is acceptable for HMR. Ensure that the reducer dispatch function is stable (as from useReducer) to avoid effect re-runs. If multiple HMR updates arrive before mount, they are applied in order, but only the last state is preserved; this matches current behavior. No breaking changes are expected, but if unexpected dispatch ordering or stale closures appear, revert this patch immediately. The patch is designed for Next.js 16.3.x/16.4.x; other branches may have a different `useActionQueue` implementation and will need adaptation.