Skip to content

Conversation

@rickhanlonii
Copy link
Member

Noticed this TODO was never implemented, so this implements support for Activity inside the deprecated, never shipping enableCreateEventHandleAPI feature used internally until we remove the flag.

Also adds a bunch of test for this API with Activity and LegacyHidden, with inline gates for the differences with the flags.

Below is the claude summary.

Details

Fix Event Handle API blur events for Activity

Problem

The beforeblur and afterblur events from the createEventHandle API were not firing when a focused element was hidden using <React.Activity>. This was because the blur check in commitBeforeMutationEffectsOnFiber only handled SuspenseComponent, not OffscreenComponent (which Activity uses internally).

Solution

Added a check for OffscreenComponent visibility transitions behind a new feature flag enableEventAPIActivityFix.


Files Modified

1. packages/react-reconciler/src/ReactFiberCommitWork.js

Added blur event handling for Activity (OffscreenComponent):

// Check if an OffscreenComponent (Activity) is being hidden with focus inside.
if (enableEventAPIActivityFix) {
  if (
    finishedWork.tag === OffscreenComponent &&
    current !== null &&
    current.memoizedState === null && // was visible
    finishedWork.memoizedState !== null && // now hidden
    doesFiberContain(finishedWork, focusedInstanceHandle)
  ) {
    shouldFireAfterActiveInstanceBlur = true;
    beforeActiveInstanceBlur(finishedWork);
  }
}

2. Feature Flag Files

Added enableEventAPIActivityFix to all feature flag files:

File Value
ReactFeatureFlags.js false (default)
ReactFeatureFlags.www-dynamic.js __VARIANT__ (dynamic)
ReactFeatureFlags.www.js imports from dynamic
ReactFeatureFlags.test-renderer.js false
ReactFeatureFlags.test-renderer.www.js false
ReactFeatureFlags.test-renderer.native-fb.js false

3. packages/react-dom/src/events/__tests__/DOMPluginEventSystem-test.internal.js

Added 22 new tests (from 120 → 142 total):

Activity tests (6 tests)

  • beforeblur and afterblur are called after a focused element is hidden
  • beforeblur and afterblur are called after a nested focused element is hidden
  • beforeblur should skip handlers from a hidden subtree after the focused element is hidden via Activity

LegacyHidden tests (6 tests)

  • beforeblur and afterblur are not called after a focused element is hidden inside LegacyHidden
  • beforeblur and afterblur are not called after a nested focused element is hidden inside LegacyHidden
  • beforeblur is not called after the focused element is hidden inside LegacyHidden

Nested Activity + LegacyHidden tests (10 tests)

Nesting Scenario Expected
<Activity><LegacyHidden> Hide outer (Activity) blur fires
<Activity><LegacyHidden> Hide inner (LegacyHidden) no blur
<Activity><LegacyHidden> Hide both simultaneously blur fires once
<Activity><LegacyHidden> Hide LegacyHidden, then Activity blur on Activity
<Activity><LegacyHidden> Hide Activity, then LegacyHidden blur on Activity, no second blur
<LegacyHidden><Activity> Hide outer (LegacyHidden) no blur
<LegacyHidden><Activity> Hide inner (Activity) blur fires
<LegacyHidden><Activity> Hide both simultaneously blur fires once
<LegacyHidden><Activity> Hide LegacyHidden, then Activity blur on Activity
<LegacyHidden><Activity> Hide Activity, then LegacyHidden blur on Activity, no second blur

Key Design Decisions

  1. Activity triggers blur, LegacyHidden does not - LegacyHidden behavior remains unchanged for backwards compatibility

  2. Inline gate pattern - Tests use if(gate('enableEventAPIActivityFix')) to test both flag states in a single test

  3. Works with enableViewTransition - The fix correctly handles the ViewTransition early exit path


Test Results

  • ✅ 142 tests pass with variant=true (enableEventAPIActivityFix ON)
  • ✅ 142 tests pass with variant=false (enableEventAPIActivityFix OFF)
  • ✅ All tests work correctly with enableViewTransition=true

@meta-cla meta-cla bot added the CLA Signed label Jan 19, 2026
@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Jan 19, 2026
@react-sizebot
Copy link

Comparing: 41b3e9a...b74caa8

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 608.48 kB 608.48 kB = 107.60 kB 107.60 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.84 kB 6.84 kB = 1.88 kB 1.88 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 670.95 kB 670.95 kB = 118.02 kB 118.02 kB
facebook-www/ReactDOM-prod.classic.js +0.05% 693.87 kB 694.20 kB +0.03% 121.97 kB 122.02 kB
facebook-www/ReactDOM-prod.modern.js +0.05% 684.25 kB 684.58 kB +0.03% 120.37 kB 120.41 kB

Significant size changes

Includes any change greater than 0.2%:

(No significant changes)

Generated by 🚫 dangerJS against b74caa8

@rickhanlonii
Copy link
Member Author

@Ranamalsingh12 future AI generated comment spam will result in a ban.

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

Labels

CLA Signed React Core Team Opened by a member of the React Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants