Skip to content

Add useTraitHiddenOnIOS feature flag#56655

Closed
SudoPlz wants to merge 1 commit into
react:mainfrom
SudoPlz:sudoplz/use-trait-hidden-on-ios
Closed

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

Conversation

@SudoPlz

@SudoPlz SudoPlz commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

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 (react#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
@javache

javache commented May 6, 2026

Copy link
Copy Markdown
Contributor

The goal of feature flags is to control rollout of experimental changes, not to provide runtime configuration.

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.

Fabric's solution to this is native state, which is stored on the ShadowNode and will persist even when the view is unmounted from the native tree. For specific components losing state, please send PR's to persist it this way.

@javache javache closed this May 6, 2026
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

2 participants