Skip to content

Add useTraitHiddenOnIOS feature flag#56655

Open
SudoPlz wants to merge 1 commit intofacebook:mainfrom
SudoPlz:sudoplz/use-trait-hidden-on-ios
Open

Add useTraitHiddenOnIOS feature flag#56655
SudoPlz wants to merge 1 commit intofacebook:mainfrom
SudoPlz:sudoplz/use-trait-hidden-on-ios

Conversation

@SudoPlz
Copy link
Copy Markdown
Contributor

@SudoPlz SudoPlz commented Apr 29, 2026

Closes #56656.

Summary:

Adds useTraitHiddenOnIOS, the iOS twin of useTraitHiddenOnAndroid. Defaults to true so iOS behaviour is unchanged.

Set it to false and Trait::Hidden subtrees stay mounted and get hidden via UIView.hidden = YES instead of being torn down via REMOVE + DELETE. That hide path has been in the codebase since 2018 — the 2020 slice-skip just makes it unreachable on iOS by default.

Why does this matter?

Custom Fabric components that mutate native state via Commands (the same way RCTTextInput.focus() and RCTScrollView.scrollTo() do) lose that state silently every time an ancestor toggles display: none. Commands have no prop-shape, so React can't replay them onto the freshly-built instance. And <Suspense> flips display: none every time it suspends — so anything below a Suspense boundary that holds Command-set state is exposed today.

10-second, 3-tap reproducer: https://github.com/SudoPlz/reproducer-react-native.

Why a flag instead of just flipping the default?

Same approach as #54112 — the existing platform behaviour stays the default, apps opt out if they need to. No surprises for anyone not actively reaching for it.

Changelog:

[IOS] [ADDED] - useTraitHiddenOnIOS feature flag (defaults to true)

Test Plan:

  • zIndexAndFlattenedNodes in StackingContextTest.cpp already exercises display: none and asserts the iOS arm via #ifdef ANDROID / #else. With the flag at its default true that assertion still holds, so existing-behaviour coverage is free.
  • New displayNoneRespectsUseTraitHiddenOnIOSOptOut overrides the flag to false and asserts the inverse: Hidden subtree stays mounted, mirroring what the Android arm already says. Skipped on Android via GTEST_SKIP because the iOS gate is unreachable there.
  • Smoke-tested on iOS Simulator against the reproducer above: bug repros with default flag; view survives the toggle with the override.

@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 29, 2026
Mirrors useTraitHiddenOnAndroid (facebook#54112) on iOS so apps can opt out of
the `Trait::Hidden` slice-skip optimization in
sliceChildShadowNodeViewPairs. When the flag is `false`, Hidden subtrees
remain in the mount slice and are hidden via `UIView.hidden = YES`
through the existing UIView+ComponentViewProtocol path (D8460108, 2018)
instead of being torn down via REMOVE + DELETE.

Default `true` so iOS behaviour is unchanged for everyone who doesn't
override it.
@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 29, 2026
@SudoPlz SudoPlz force-pushed the sudoplz/use-trait-hidden-on-ios branch from 1253d1e to 8dcdb70 Compare April 29, 2026 21:56
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.

iOS Fabric: display: none silently destroys host-component, instead of just hiding or removing it

1 participant