Skip to content

MSD: Refactor interim omnibar to hooks-based data flow#109881

Open
StevenDufresne wants to merge 7 commits intotrunkfrom
refactor/interim-omnibar-hooks-data-flow
Open

MSD: Refactor interim omnibar to hooks-based data flow#109881
StevenDufresne wants to merge 7 commits intotrunkfrom
refactor/interim-omnibar-hooks-data-flow

Conversation

@StevenDufresne
Copy link
Copy Markdown
Contributor

@StevenDufresne StevenDufresne commented Apr 8, 2026

Proposed Changes

  • Refactor loadOmnibar to mount a hooks-based container (useInterimOmnibarData) instead of driving React imperatively with root.render and QueryObserver.subscribe after hydrateRoot.
  • Add unit tests for the new hook: pre-hydration SSR-matching shape, post-hydration callback wiring, recent-site loading, and the primary_blog fallback.

Why are these changes being made?

The previous implementation raced hydration and caused React to bail with:

This root received an early update, before anything was able to hydrate. Switched the entire root to client rendering.

With the refactor, every post-hydration update flows through normal React rendering — there is no external code path that can update the root between hydrateRoot and its commit.

Testing Instructions

  1. Enable the dashboard/omnibar feature flag.
  2. Hard-reload a Dashboard page with the omnibar visible.
  3. Confirm the "This root received an early update…" warning no longer appears in the console.
  4. Confirm the omnibar still shows the current user and the active site, and re-renders when switching sites.
  5. Confirm the mobile menu and notifications toggles still work.

Pre-merge Checklist

  • Has the general commit checklist been followed? (PCYsg-hS-p2)
  • Have you written new tests for your changes?
  • Have you tested the feature in Simple (P9HQHe-k8-p2), Atomic (P9HQHe-jW-p2), and self-hosted Jetpack sites (PCYsg-g6b-p2)?
  • Have you checked for TypeScript, React or other console errors?
  • Have you tested accessibility for your changes? Ensure the feature remains usable with various user agents (e.g., browsers), interfaces (e.g., keyboard navigation), and assistive technologies (e.g., screen readers) (PCYsg-S3g-p2).
  • Have you used memoizing on expensive computations?
  • Have we added the "[Status] String Freeze" label as soon as any new strings were ready for translation (p4TIVU-5Jq-p2)?
  • For changes affecting Jetpack: Have we added the "[Status] Needs Privacy Updates" label if this pull request changes what data or activity we track or use (p4TIVU-aUh-p2)?

The previous implementation drove React imperatively via `root.render`
and `QueryObserver.subscribe` after `hydrateRoot`, which raced hydration
and caused React to bail out with "This root received an early update,
before anything was able to hydrate. Switched the entire root to client
rendering."

Move the data fetching into a new `InterimOmnibarContainer` that uses
TanStack Query hooks (`useQuery` for auth, recentSites, and the active
site). A `useState(false)` + `useEffect` gate ensures the first render
mirrors the SSR output exactly (`user = initialUser`, `site = null`, no
toggle callbacks); after hydration commits, the container switches to
hook-driven data and wires up the toggle handlers. All post-hydration
updates now flow through normal React rendering instead of external
imperative calls.

`loadOmnibar` shrinks to just click delegation and a single
`hydrateRoot` call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@matticbot
Copy link
Copy Markdown
Contributor

matticbot commented Apr 8, 2026

This PR modifies the release build for the following Calypso Apps:

For info about this notification, see here: PCYsg-OT6-p2

  • help-center
  • notifications
  • odyssey-stats

To test WordPress.com changes, run install-plugin.sh $pluginSlug refactor/interim-omnibar-hooks-data-flow on your sandbox.

StevenDufresne and others added 3 commits April 8, 2026 14:13
Extract the data-flow logic into an exported `useInterimOmnibarData`
hook so it can be tested in isolation without rendering the full
masterbar tree. Add unit tests covering: bootstrapped user via
initialData, toggle callback wiring, recent-site loading, primary_blog
fallback, and the null-site state when no siteId is known.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the weak assertions (`initialData` pass-through and the
`site === null` default) with a `renderToStaticMarkup`-based test that
verifies the hook's pre-hydration output matches the SSR shape exactly
(user, null site, no callbacks). This is the contract hydration
actually depends on.

Drop the "keeps site null" test — it only asserted a destructuring
default and would have passed even if the enabled guard was removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the hook out of `interim-omnibar-container.tsx` into
`use-interim-omnibar-data.ts`, matching the source-file naming
convention used by `hooks/use-persistent-view.ts` (and lining up with
the test file name).

Tests now build a fresh `OmnibarEvents` per test instead of subscribing
to the shared module singleton, so a failed assertion can't leak a
dangling subscription into later tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@StevenDufresne StevenDufresne marked this pull request as ready for review April 8, 2026 05:55
@StevenDufresne StevenDufresne requested a review from a team as a code owner April 8, 2026 05:55
@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Apr 8, 2026
Comment on lines +76 to +78
afterEach( () => {
nock.cleanAll();
} );
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This afterEach can be removed — nock.cleanAll() is already called globally after each test.

Note: nock.cleanAll() is already called globally after each test, so you don't need afterEach( () => nock.cleanAll() ) in your test files.

docs/testing.md

Suggested change
afterEach( () => {
nock.cleanAll();
} );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants