Skip to content

Sudoplz/ios display hidden mounting#56649

Closed
SudoPlz wants to merge 335 commits intofacebook:mainfrom
discord:sudoplz/ios-display-hidden-mounting
Closed

Sudoplz/ios display hidden mounting#56649
SudoPlz wants to merge 335 commits intofacebook:mainfrom
discord:sudoplz/ios-display-hidden-mounting

Conversation

@SudoPlz
Copy link
Copy Markdown
Contributor

@SudoPlz SudoPlz commented Apr 28, 2026

Summary:

Adds useTraitHiddenOnIOS feature flag - the iOS twin of Meta's
useTraitHiddenOnAndroid (#54112). Defaults to true so iOS behavior
is unchanged for anyone who doesn't override it.

RN's been carrying the wiring to hide views instead of destroying
them since 2018. A 2020 optimization started filtering hidden subtrees out of the mounting slice before they could reach that path.

This flag re-opens it on demand.

Next

  • Follow-up app PR (after we bump the RN hash) flips the flag to
    false in our RCTAppDelegate and ships the fix.

What's in this PR

  • 1-line gate in sliceChildShadowNodeViewPairs.cpp (the only important code change).
  • Flag declared in ReactNativeFeatureFlags.config.js,
    defaultValue: true plus ~20 auto-generated files needed for the flag

react-native-bot and others added 30 commits September 10, 2025 13:51
#publish-packages-to-npm&0.81-stable
…chronously (#14)

* expose `flush` on RCTWebSocketModule to close all open websockets synchronously

add log statement back

* simplify code

* simplify even more

* change comment
For some reason the in-repo version isn't where this script it expects it to be (it's at `src` in our repo, whereas the script is looking in `lib`). This is likely due to us pulling down the source code directly whereas the script expects it to have gone through some build process first. In any case, this updates thes cript to prefer the version provided via NPM which does tend to work.
…cker.

Add ability to override mSource of the image source.
This fixes an issues where the fabric renderer would get the correct
sizing via onSizeChanged, write it to the shadow tree generating a state
update callback and then setting the wrong size using ModalHostManager.
Once onSizeChanged is fired by the OS it is treated as the definitive
source of truth for sizing.
These podspecs all depend on a function defined in another Ruby file
(which is kind of scary, but prooobably okay). However, they do not
`require` that file, and instead (previously) were just relying on the
fact that CocoaPods loads all the podspecs declared in your Podfile.

This is fine for `pod install`, but doesn't apply to commands like `pod
ipc spec` (which is used to convert an individual Podspec file to JSON).
With that command, the PodSpecs were not evaluating successfully, as
they didn't have access to this function.

So we add the proper requires here and call it a day.
bmarimon and others added 23 commits April 13, 2026 15:33
[fix] set background correctly in setFeedbackUnderlay
onHostPause already checks activeActivities and skips pausing when
other activities are still active. onHostDestroy lacked this check,
so destroying a secondary activity while the primary was still alive
would move the shared ReactContext to BEFORE_CREATE. If the primary
was never paused, no subsequent onHostResume would fire, leaving the
context permanently destroyed and unresponsive to input.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…os-part

feat: implement ios part of measureOnUI
## Summary:

Mirrors useTraitHiddenOnAndroid from facebook#54112. Lets iOS apps opt out of
the `Trait::Hidden` slice-skip; Android can already opt in. Default is
`true`, preserving today's iOS behavior.

What the optimization does

D22134220 (2020, "Fabric: display: none nodes do not create views
anymore") added `Trait::Hidden` and a slice-skip in
`sliceChildShadowNodeViewPairs` that filters out subtrees whose Yoga
display is None. On iOS the diff then issues REMOVE + DELETE for the
entire subtree — invisible content stops costing anything.

For most UIs this is a clean win.

Where it bites

The optimization assumes that `display: none` transitions are rare.
That breaks for a specific pattern: **a custom host component mounted
under `<Suspense>`**.

Each suspend → resume cycle, Suspense flips display between `flex` and
`none`. With the optimization on, every cycle:

- tears down the entire subtree of UIViews,
- drops per-instance native state — measurement caches, scroll
  position, animation drivers, anything internal to the host
  component,
- re-runs `init`/`dealloc` and rebuilds the subtree on resume.

Suspend/resume ends up heavier than a fresh mount, and any state the
component held disappears between renders.

What the flag does

**It re-activates an existing code path. Nothing in the mounting
layer is new.**

The hide-via-`UIView.hidden` wiring shipped in D8460108 (June 2018,
"Fabric: Default support of displayType and layoutDirection
layout...") and has lived in `UIView+ComponentViewProtocol
updateLayoutMetrics:` ever since — a few lines below the slice
consumer in the same file. For two years it was the only iOS path;
the 2020 slice-skip didn't replace it, it just made it unreachable
in the common case.

Setting the flag to `false` lets Hidden shadow nodes pass through
the slice. The differ emits an `UPDATE_LAYOUT_METRICS` mutation
with `displayType == None`, and the 2018 wiring picks it up:
`self.hidden = YES` on the underlying UIView. Same view, hidden in
place.

Defaults

- `useTraitHiddenOnIOS = true` — keeps the iOS behavior introduced
  in 2020.
- `useTraitHiddenOnAndroid = false` — keeps the Android behavior,
  which never adopted the slice-skip.

Both flags share the same semantic ("use the optimization"); the
defaults encode each platform's pre-flag behavior. Flipping either
default is out of scope.

## Changelog:

[IOS] [ADDED] - useTraitHiddenOnIOS feature flag to opt out of the `display: none` slice-skip optimization

## Test Plan:

No new tests. `StackingContextTest` exercises the slice-skip path and
passes unchanged with the flag at its default `true`. Manually
flipping the flag to `false` produces the existing Android branch's
expected view tree (8 views, Hidden subtrees preserved with
`self.hidden = YES`).
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 28, 2026
@github-actions
Copy link
Copy Markdown

Warning

Missing Test Plan

Please add a "## Test Plan" section to your PR description. A Test Plan lets us know how these changes were tested.

Caution

Missing Changelog

Please add a Changelog to your PR description. See Changelog format

@github-actions
Copy link
Copy Markdown

Warning

JavaScript API change detected

This PR commits an update to ReactNativeApi.d.ts, indicating a change to React Native's public JavaScript API.

  • Please include a clear changelog message.
  • This change will be subject to additional review.

This change was flagged as: BREAKING

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Apr 28, 2026
@SudoPlz
Copy link
Copy Markdown
Contributor Author

SudoPlz commented Apr 29, 2026

Superseded by #56655, which is opened from a clean branch off current main on a personal fork (this branch was based on a stale fork point of react-native and the diff was swallowing version drift). Closing this in favour of #56655.

@SudoPlz SudoPlz closed this Apr 29, 2026
@SudoPlz SudoPlz deleted the sudoplz/ios-display-hidden-mounting branch April 29, 2026 23:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.