Skip to content

Wolewicki/scrollview perf improvements#6

Closed
WoLewicki wants to merge 28 commits into
discord:mainfrom
WoLewicki:wolewicki/scrollview-perf-improvements
Closed

Wolewicki/scrollview perf improvements#6
WoLewicki wants to merge 28 commits into
discord:mainfrom
WoLewicki:wolewicki/scrollview-perf-improvements

Conversation

@WoLewicki

Copy link
Copy Markdown

Summary:

On the New Architecture (Fabric), Reanimated commits the shadow tree ~once per animation frame, and each mount transaction unconditionally called _remountChildren, re-clipping the entire descendant subtree even when nothing scrolled or changed layout.

Guard _remountChildren with RCTMountingTransactionAffectsClipping, which returns YES only when the transaction can change clipping: an Insert/Remove mutation, an Update with changed layoutMetrics, or an Update toggling removeClippedSubviews. maintainVisibleContentPosition is unaffected since _adjustForMaintainVisibleContentPosition still runs on every mount.

Changelog:

[IOS] [CHANGED] - dont remount scrollview children when not necessary

Test Plan:

See that the clipping still works correctly on large ScrollView and there is less CPU consumed (e.g. in Xcode Time Profiler).

huntie and others added 28 commits June 30, 2026 08:39
Summary:
Pull Request resolved: react#57382

**Context**

Strict TypeScript API readiness: High quality inline docs should reach users via TypeScript in their IDEs.

**This diff**

I asked Claude to crawl reactnative.dev and insert JSDoc comments into our core components, based on some manual-pass starting points.

This is also a quality pass on all modules touched, standardising JSDoc use, and in some cases merging/removing redundant information (preferring the docs we’ve maintained more carefully on the website).

Commit 1 of 2 (Components).

Replaces react#57380.

Changelog: [Internal]

___

Differential Revision: D110195869

fbshipit-source-id: a60cded5a0c599810c80d174b7de085903cd6701
Summary:
Pull Request resolved: react#57383

**Context**

Strict TypeScript API readiness: High quality inline docs should reach users via TypeScript in their IDEs.

**This diff**

I asked Claude to crawl reactnative.dev and insert JSDoc comments into our core components, based on some manual-pass starting points.

This is also a quality pass on all modules touched, standardising JSDoc use, and in some cases merging/removing redundant information (preferring the docs we’ve maintained more carefully on the website).

**Manual edits**

- `StyleSheet`

Commit 2 of 2 (APIs).

Replaces react#57380.

Changelog:
[General][Added] - Strict TypeScript API: Component and API doc comment coverage has been significantly improved

___

Differential Revision: D110195868

fbshipit-source-id: 7d8880352c934702671f89a403f334e30b1e749c
Summary:
Pull Request resolved: react#57280

Updates Fresco from 3.6.0 to 3.7.0. Picks up a few new features and bug fixes. Full changelog: https://github.com/facebook/fresco/releases/tag/v3.7.0

Changelog: [Internal]

Reviewed By: cortinico, fkgozali

Differential Revision: D109029522

fbshipit-source-id: 2c685d4e41eb8bd3c8668db666d855deb1f50170
…n package (react#57378)

Summary:
Pull Request resolved: react#57378

Refactor Metro imports to:
 - Remove use of deprecated default object export, prefer named/namespace exports.
 - Prefer imports from `metro`, remove public dependencies on `metro-core` and `metro-config` from `react-native/community-cli-plugin`.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D110177050

fbshipit-source-id: f21a561d40d13f354bc56cdc50d6e6d6843877cc
)

Summary:
`FileReader`'s `readAsText` / `readAsDataURL` / `readAsArrayBuffer` go straight from `EMPTY` to `DONE` — they never set `readyState` to `LOADING`. Two consequences:

1. `readyState` is never observably `LOADING` during a read (it should be, per the [W3C FileReader spec](https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState)).
2. `abort()` is dead during a read. Its body only emits events when the reader is mid-read:

   ```js
   abort() {
     this._aborted = true;
     // only call onreadystatechange if there is something to abort, as per spec
     if (this._readyState !== EMPTY && this._readyState !== DONE) {
       this._reset();
       this._setReadyState(DONE);
     }
     this._reset();
   }
   ```

   Because a read never sets `LOADING`, `readyState` is still `EMPTY` when `abort()` runs, so the guard is false and **no `abort`/`loadend` events are dispatched** — even though the method is written specifically to handle that case.

## Fix

Set the `LOADING` state when each read starts (via the existing `_setReadyState` helper, which also fires `readystatechange`). This makes `readyState` correct during a read and makes `abort()` during a read dispatch `abort`/`loadend` as its own code already anticipates.

Changelog: [General] [Fixed] - Set `FileReader` `readyState` to `LOADING` during a read so `abort()` correctly emits `abort`/`loadend`

Pull Request resolved: react#57375

Test Plan:
Added tests to `Libraries/Blob/__tests__/FileReader-test.js`:
- `readyState` is `LOADING` synchronously after a read starts.
- `abort()` during a read dispatches `abort` and `loadend`.

Both fail before this change and pass after; the existing read tests and the full Blob test suite still pass (20/20). ESLint clean on the changed files.

## Changelog:

[General] [Fixed] - Set `FileReader` `readyState` to `LOADING` during a read so `abort()` correctly emits `abort`/`loadend`

Reviewed By: javache

Differential Revision: D110191434

Pulled By: Abbondanzo

fbshipit-source-id: 2ca2c4bb7fc26a6d5939a67ee79fc736a5927d0e
react#57390)

Summary: Pull Request resolved: react#57390

Reviewed By: javache

Differential Revision: D110188986

fbshipit-source-id: 466f3e5f89bdd54dfe7079db615d30a4e82802bc
Summary:
## Summary:  Do not synchronize on java.lang.Boolean.

Once JEP 401 is implemented synchronization on
value classes (which box classes are) will throw IdentityException.
In general synchronization on 1) mutable field 2) box class is
inefficient (instances are shared) at best and a bug at worst case
as synchronization is done holding different monitors (it might be
fine, but is hard to reason about).

## Changelog:

[ANDROID] [FIXED] Use explicit `ReactInstanceManager.mHasStartedDestroyingLock` instead of using `ReactInstanceManager.mHasStartedDestroying`

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: react#56196

Test Plan: GHA

Reviewed By: cortinico

Differential Revision: D110168816

Pulled By: javache

fbshipit-source-id: dae081dc00a94859f19dcc3607971a9c3d6fbd44
Summary:
`Blob.slice(start, end)` is modeled on the [W3C Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice), where both `start` and `end` may be negative (relative to the end of the blob) and an `end` that precedes `start` yields an empty blob.

The current implementation handles a negative `end` but not a negative `start`:

```js
if (typeof start === 'number') {
  if (start > size) { start = size; }   // only the upper bound is checked
  offset += start;
  size -= start;
  if (typeof end === 'number') {
    if (end < 0) { end = this.size + end; }  // negative end IS handled
    if (end > this.size) { end = this.size; }
    size = end - start;                       // not clamped to >= 0
  }
}
```

So:

- `blob.slice(-100)` sets `offset` to a **negative** value and makes `size` *larger* than the blob, instead of slicing the last 100 bytes.
- `blob.slice(200, 100)` (end before start) produces a **negative** size.

## Fix

- Normalize a negative `start` relative to the blob size (`Math.max(this.size + start, 0)`), mirroring the existing `end` handling.
- Clamp the resulting size with `Math.max(end - start, 0)` so an inverted range yields an empty blob.

Changelog: [General][Fixed] Fix Blob.slice() for negative start and inverted ranges

Pull Request resolved: react#57374

Test Plan:
Added tests to `Libraries/Blob/__tests__/Blob-test.js` for negative start, negative end, and end-precedes-start. The negative-start and inverted-range cases fail before this change (negative offset / negative size) and pass after; existing slice tests and the rest of the suite still pass (8/8). ESLint clean on the changed files.

## Changelog:

[General] [Fixed] - Fix `Blob.slice()` for negative start offsets and inverted ranges

Reviewed By: cortinico

Differential Revision: D110173869

Pulled By: javache

fbshipit-source-id: 9f15bfa9c7d8fbba3f7f3b2473c3b39c9cf9d2fe
…act#57251)

Summary:
On iOS, both `Alert` and `Modal` could render in the top-left corner of the screen instead of filling it.

- `RCTAlertController`: when the alert window is created from a `UIWindowScene`, it was initialized without a frame and defaulted to a zero-sized frame. We now set its frame to the scene's coordinate space bounds.
- `ModalHostViewUtils`: `ModalHostViewScreenSize` used `RCTScreenSize()`, whose value is cached once via `dispatch_once`. If that first read happens before the screen is ready, it caches `0` and stays `0`, so the modal renders zero-sized in the top-left corner. We now read `RCTViewportSize()` on the main queue, which reads the key window's bounds live.

Fixes react#45453

## Changelog:

[IOS] [FIXED] - Prevent Alert and Modal from rendering in the top-left corner

Pull Request resolved: react#57251

Test Plan:
- Open an `Alert` on iOS and confirm it is centered and correctly sized.
- Open a `Modal` on iOS and confirm it fills the screen instead of appearing in the top-left corner.

## Screenshots:

<img width="284" height="542" alt="Alert" src="https://github.com/user-attachments/assets/e98ae159-9752-4b6e-8a97-0276984af3ba" />

<img width="284" height="542" alt="Modal" src="https://github.com/user-attachments/assets/1fff4217-5f28-4017-8699-01b757e7fb8a" />

Reviewed By: javache

Differential Revision: D109310703

Pulled By: cortinico

fbshipit-source-id: db3d66a704921c524faed62d82695528d75698eb
Summary:
Pull Request resolved: react#57364

The single-op batching guard now lives entirely in `NativeAnimatedHelper`, where `isSingleOpBatching` is gated on `Platform.OS === 'android'`, `queueAndExecuteBatchedOperations` being available, and `!cxxNativeAnimatedEnabled()`. The `animatedShouldUseSingleOp` flag defaulted to `true` and no longer carries any of that logic, so it is fully redundant. Remove the flag from the config, drop its now-vestigial term from `isSingleOpBatching` in both `NativeAnimatedHelper.js` and the macOS variant, and regenerate `ReactNativeFeatureFlags.js`.

This is behavior-preserving: with the flag at its default `true`, `isSingleOpBatching` evaluates identically to before on every platform.

Changelog:
[Internal]

Reviewed By: javache

Differential Revision: D109570192

fbshipit-source-id: dbd3a4f58dd5c1edafcbf8a8e7a015a6eb72aea9
Summary:
Removes the deprecated `animated` prop from `Modal`. It was a no-op everywhere. Use `animationType` instead.

See react#57384

## Changelog:

[GENERAL] [REMOVED] - Remove deprecated `Modal` `animated` prop

Pull Request resolved: react#57385

Test Plan:
- `yarn jest packages/react-native/Libraries/Modal`
- `yarn flow` and `tsc` pass with the prop removed from `Modal.js` / `Modal.d.ts`.

Reviewed By: huntie

Differential Revision: D110205204

Pulled By: cortinico

fbshipit-source-id: 8e5b4d7dc8811d7270de3776476e7f868eafddd5
Summary:
Closes react#45255.

Adds `textAlign: 'start' | 'end'` support for Text and TextInput across the JS types, Android, iOS, and Fabric text conversion paths.

- Android legacy Text and TextInput now accept logical `start`/`end` alignment values.
- Fabric preserves `start`/`end` as distinct `TextAlignment` values and resolves them against layout direction for iOS paragraph layout.
- Existing `left`/`right` behavior is left unchanged to avoid changing current RTL semantics.

This replaces react#57007 because the original fork became locked and could not be updated after the upstream conflict.

## Changelog:

[GENERAL] [ADDED] - Add support for `textAlign: 'start'` and `textAlign: 'end'`.

Pull Request resolved: react#57201

Test Plan:
- `yarn build-types`
- `yarn test-typescript`
- `yarn flow-check`
- `./node_modules/.bin/prettier --check packages/react-native/Libraries/Components/TextInput/TextInput.d.ts packages/react-native/Libraries/Components/TextInput/TextInput.flow.js packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js packages/react-native/types/__typetests__/index.tsx packages/react-native/ReactNativeApi.d.ts`
- `./gradlew ktfmtCheck -Preact.internal.useHermesStable=true --no-daemon`
- `./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest --tests com.facebook.react.views.textinput.ReactTextInputPropertyTest.testTextAlign --tests com.facebook.react.views.text.TextAttributePropsTest -Preact.internal.useHermesStable=true --no-daemon`
- `./gradlew ':packages:react-native:ReactAndroid:buildCMakeDebug[arm64-v8a][hermestooling,jsi,etc]' -Preact.internal.useHermesStable=true --no-daemon`
- `git diff --check`

The Android unit test and CMake checks were run from an ASCII-only temporary worktree because Kotlin unit test compilation in my main checkout fails before running these tests when the workspace path contains non-ASCII characters.

Reviewed By: christophpurrer

Differential Revision: D108628602

Pulled By: javache

fbshipit-source-id: 28111d0553451d7de0424a07e956f71bda8787ca
Summary:
Pull Request resolved: react#57377

ViewShadowNodeProps was a thin subclass of ViewProps that only forwarded its constructor. After the feature flag propagation logic was removed, the subclass serves no purpose.

Changelog:
[Internal]

Reviewed By: lenaic

Differential Revision: D110095190

fbshipit-source-id: a2555c4ba455e51116c73be3b5c9a71788895ffd
Summary:
Fixes `LogBox` layout while using accessibility font multiplier. I was aiming for the highest possible value without breaking the UI. Max scales are mostly set to 1.5 and less when needed.

<img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2026-05-04 at 19 39 35" src="https://github.com/user-attachments/assets/f59b426b-44fe-4ad0-bb52-2e9ee286073e" />

<img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2026-05-04 at 19 39 47" src="https://github.com/user-attachments/assets/b4b4d871-f21b-45b2-accf-be1b58e6c881" />

<img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2026-05-04 at 19 39 40" src="https://github.com/user-attachments/assets/006dd5bb-4525-491c-8f3f-ddcd211615da" />

<img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2026-05-04 at 19 41 01" src="https://github.com/user-attachments/assets/d7029511-878c-4c0b-a7d0-b7c7a6abaa9c" />

https://github.com/user-attachments/assets/507ba38d-6338-4e33-b3d0-cbf54e108ac3

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[GENERAL] [FIXED] - Set max font scaling in `LogBox` to avoid layout breaking

Pull Request resolved: react#57333

Test Plan:
1. Set the max text size on your testing device.
2. Log or throw an error in the playground.

Reviewed By: ellemac123

Differential Revision: D110045786

Pulled By: javache

fbshipit-source-id: cdd94437a54443a04d724f250c4b5ac1da059b34
…eact#57381)

Summary:
Pull Request resolved: react#57381

`AbortSignal.any` previously processed the input signals in a single pass, attaching an abort listener to each signal as it iterated and unwinding them with a `cleanup()` call whenever it later encountered an already-aborted (or invalid) signal. This deviates from the specification, which validates the whole list, then scans it for an already-aborted signal (returning an aborted signal immediately), and only then subscribes to the remaining signals.

Process the list in three passes instead:

- The first pass validates that every entry is an `AbortSignal`, throwing before any other work so an invalid entry never short-circuits ahead of an earlier already-aborted one.
- The second pass returns an already-aborted signal (via `AbortSignal.abort(reason)`) if any input is already aborted. No listeners are registered yet, so there is nothing to unwind on an early return.
- The third pass subscribes to every signal, since all of them are valid and none is aborted at that point.

This removes the need to clean up partially-registered listeners mid-setup and matches the behavior described in the DOM specification.

Also remove a dead branch in `AbortController`'s `getSignal` helper. The `controller === null ? 'null' : typeof controller` expression was unreachable for `null` input because the preceding `controller[SIGNAL_KEY]` access already throws a `TypeError` on `null`/`undefined`, and it only existed by suppressing a Flow `invalid-compare` error. Use `typeof controller` directly so the suppression is no longer needed.

Changelog:
[General][Fixed] - Make `AbortSignal.any()` process its input signals in multiple passes to match the DOM specification

Reviewed By: javache

Differential Revision: D110185361

fbshipit-source-id: 633a8ce4f8a2b2a892eccffb644d94ce705a3449
Summary:
Pull Request resolved: react#57376

AnimatedPropsRegistry::update() runs on the UI thread every animation frame and
created a surface's entry via operator[]. clearOnSurfaceStop() (run on the JS
thread when a surface stops) erases that entry, but an in-flight animation frame
landing after the stop re-created it via operator[] -- and since the surface is
gone, nothing ever cleans it up again. The resurrected entry leaks its
PropsSnapshot and ShadowNodeFamily for the lifetime of the registry.

A surface's entry is legitimately created by getMap(), which
AnimationBackendCommitHook calls on every React commit. stopSurface drains
in-flight commits before unregistering the ShadowTree
(ShadowTreeRegistry::remove takes the registry's unique lock, which excludes the
shared-locked commit visits), so getMap() can never run for a stopped surface.
That leaves update()'s operator[] as the only thing that can resurrect one.

Fix: update() now only refines surfaces that already exist (find instead of
operator[]) and never creates an entry; getMap() remains the sole creator. A
stopped surface can no longer be resurrected, and there is no extra bookkeeping
that could grow over time.

Changelog: [General][Fixed] - Fix a surface-stop race in the C++ Animated shared backend that could permanently leak per-surface animated state

Reviewed By: javache

Differential Revision: D109156094

fbshipit-source-id: 4684ca51d372023e3b082427a225de7d84d14889
Summary:
Pull Request resolved: react#57387

Adds `yarn fantom-cli`, an interactive REPL that evaluates JavaScript against the same native tester binary and Hermes runtime that Fantom tests run in. State persists across lines, input goes through Metro/Babel (so `import`, JSX and Flow all work), and the environment is set up the same way tests set it up — `React`, `ReactNative` and the `Fantom` API are available globally, so you can render surfaces and drive them interactively from the prompt.

The native tester gains an `--interactive` mode that loads a warm-up bundle without running tests and then evaluates length-prefixed snippets read from stdin, reporting results, console output and errors back as newline-delimited JSON. A Node driver hosts a Metro server, builds the warm-up bundle, spawns the binary, and bridges each line of input into the live runtime (top-level declarations persist across evaluations).

Features:

- Console-style output: results are printed with an inspector similar to the Chrome DevTools / Node.js consoles (nested objects/arrays up to a depth limit, quoted strings, functions/classes, `Map`/`Set`/`RegExp`/`Date`/`Error`, class instances, circular references, multi-line wrapping), colorized by type when stdout is a terminal. Inspecting a property never aborts the result or leaks into later evaluations: a property whose getter fails renders as `[Thrown: <error>]`, including getters that fail asynchronously through the runtime's global error handler (e.g. accessing a `react-native` export backed by a TurboModule that isn't registered).
- Autocompletion: pressing Tab completes global identifiers, in-scope bindings and object properties (property names are listed without invoking getters).
- Node-like CLI: with no arguments it starts the REPL; `-e <code>` evaluates a snippet and exits; a filename runs that script and exits. In the non-interactive modes the value of a trailing expression is not printed (use `console.log`) and a thrown error exits with a non-zero status code.

Also documents the REPL in the Fantom README.

Changelog: [Internal]

Reviewed By: javache, sammy-SC

Differential Revision: D110187712

fbshipit-source-id: 3e76c498ae04b8b1ce9e0e27e5ce6b6a0cd11e09
…eact#57401)

Summary:
Pull Request resolved: react#57401

The `FANTOM_ENABLE_CPP_DEBUGGING` environment variable was kept only as a legacy alias for `FANTOM_DEBUG_CPP`. Remove it from Fantom so `FANTOM_DEBUG_CPP` is the single supported way to enable the C++ debugger.

Changelog: [Internal]

Reviewed By: sammy-SC

Differential Revision: D110324557

fbshipit-source-id: 2cbde4736137db792b8b99d1806c146e3094ca4b
…57386)

Summary:
Removes support for the deprecated boolean values of `ScrollView`'s `keyboardShouldPersistTaps` prop. `true`/`false` have long been deprecated in favor of the string values `'always'`/`'never'` (with `'handled'` as a third option), and a runtime warning was already emitted when a boolean was passed. This drops the boolean from the type and removes the now-unused boolean handling.

Migration:
- `keyboardShouldPersistTaps={true}` → `keyboardShouldPersistTaps="always"`
- `keyboardShouldPersistTaps={false}` → `keyboardShouldPersistTaps="never"`

See react#57384

## Changelog:

[GENERAL] [BREAKING] - Remove deprecated boolean values support for `ScrollView` `keyboardShouldPersistTaps`

Pull Request resolved: react#57386

Test Plan:
- `yarn flow` passes (the previously-needed `$FlowFixMe[sketchy-null-bool]` suppressions are gone).
- `yarn build-types` regenerates `ReactNativeApi.d.ts` with `keyboardShouldPersistTaps?: "always" | "handled" | "never"`.
- RNTester ScrollView "Keyboard" example still toggles between `never`/`always`/`handled`.

Reviewed By: cortinico, rozele

Differential Revision: D110218873

Pulled By: Abbondanzo

fbshipit-source-id: 2cb86fe8b058432795cddc3f4f2c2cb1b1fd1fdf
…eact#57398)

Summary:
Pull Request resolved: react#57398

`buckets_.data()` and `dynamicData_.data()` return `nullptr` when the vector is empty, and glibc marks `memcpy`'s src argument as `nonnull`. Passing `nullptr` is UB even with a size of 0, which trips UBSan halt-on-error on any MapBuffer that has no buckets (`EMPTY()`) or no dynamic-data entries (scalar-only maps). Guard both memcpys with an empty check.

Changelog:
[General][Fixed] - Avoid `memcpy(_, nullptr, 0)` UB in `MapBufferBuilder::build` for empty / scalar-only MapBuffers

Reviewed By: cortinico

Differential Revision: D110316404

fbshipit-source-id: 2e58acf7ec59d222951c5846bcf88a369ad8ef87
Summary:
The C++ native animated backend (`cxxNativeAnimatedEnabled`) prevents the `useNativeDriver` first-frame flash by having `AnimatedMountingOverrideDelegate` re-merge a view's live animated props (`getManagedProps`) onto mount `Update` mutations, so a stale JS re-render can't reach the screen. There is a residual at connect time: `connectAnimatedNodeToView` registers the view (the override starts overriding it) but never runs the props node, so `getManagedProps()` returns an empty object until the next animation frame. If a Fabric mount transaction is pulled in that window the override has nothing to merge and the un-driven default value flashes for one frame, most visibly when a brand-new view is connected to an already mid-flight shared `Animated.Value`.

This seeds the props node with `node->update()` at the end of `connectAnimatedNodeToView` so `getManagedProps()` is live the instant the view becomes managed. No manager mutex is held at that point and `update()` takes only the node-local props mutex, so it is lock-safe; it is idempotent with the per-frame update and adds no extra Fabric commit because it only stages. It closes the connect-while-`Update`-in-flight case and mitigates the brand-new-view (`Insert`) first-paint case; the override only rewrites `Update`/`Delete` mutations today, so fully closing `Insert` is a follow-up. It also adds the missing unit coverage for the `getManagedProps` / `hasManagedProps` seam the override depends on.

## Changelog:

[INTERNAL] [FIXED] - Seed managed props when a view connects to the native animated backend so they are live before the first frame, shrinking the useNativeDriver mount flash

Pull Request resolved: react#57391

Test Plan: Adds `ManagedPropsMountingOverrideTests.cpp` to the ReactCommon animated unit tests. `getManagedPropsReflectsLiveValueAcrossFrames`, `getManagedPropsNullForUnconnectedView`, `hasManagedPropsTracksConnectAndDisconnect` and `getManagedPropsIsolatedPerView` pin the seam and pass regardless of the change. `getManagedPropsLiveImmediatelyOnConnect` and `getManagedPropsLiveOnConnectWhileValueMidFlight` are the regression cases: they fail without the seed (empty props at connect) and pass with it. Run via the animated C++ unit test target. On-device verification (iOS and Android with `cxxNativeAnimatedEnabled` on, frame-stepped capture of a popover/menu open) is still recommended for the user-visible flash.

Reviewed By: sammy-SC

Differential Revision: D110323404

Pulled By: zeyap

fbshipit-source-id: 485e31e1c874548da1ee7339481164faa763b2d9
…#52646)

Summary:
Some third-party libraries, like react-native-reanimated, can clone nodes in a different thread while react-native is calling `setNativeProps_DEPRECATED`. This results in a race condition, where a stale pointer to `nativeProps_DEPRECATED` can be accessed, resulting in a crash. This usually manifests as a `EXC_BAD_ACCESS` crash on iOS. On Android it seems more rare. We've added a lock around accesses to nativeProps_DEPRECATED, but alternative options of fixing this can be considered too.

For more information see software-mansion/react-native-reanimated#7666

## Changelog:

[INTERNAL] [FIXED] - Fixed crashes caused by race conditions when third-party libraries clone the shadow dom from a different thread

Pull Request resolved: react#52646

Test Plan:
Due to this being a race condition that only manifests in rare circumstances, it's very difficult to create a reliable reproduction case. The issue mentioned above contains ThreadSanitizer logs that demonstrate this issue. TSan no longer complains with this patch applied, and we've not seen any additional issues from it after deploying it in production over the past week.

Added unit test covering the `nativeProps_DEPRECATED` merge logic in `UIManager::cloneNode` and `ShadowNode::clone`:

```
buck2 test //xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/uimanager:tests -- --regex FabricUIManagerTest
```

Reviewed By: zeyap

Differential Revision: D110169424

Pulled By: javache

fbshipit-source-id: 6139253dcc2c33348a0c1a3bd01e695d15aa83bc
Summary:
Pull Request resolved: react#57400

In this diff we drop the custom choreographer for the backend on Android and instead plug into the one in `FabricUIManager`. This reduces the area for possible mistakes, makes it clearer how Fabric interacts with animation on a per-frame basis, and simplifies the flow around invalidation and cleanup of the React instance.

The crash this is meant to avoid comes from having two separate frame callback lifecycles. The old `AnimationBackendChoreographer` owned a self-reposting callback that could keep driving `FabricUIManagerBinding.driveAnimationBackend` independently from Fabric's own lifecycle. During React instance teardown, `FabricUIManager.invalidate()` pauses Fabric's frame callback and then unregisters the native binding. If a separate backend callback survives that sequence, it can invoke the binding after the native side has been uninstalled.

The shared animation backend is now driven from Fabric's existing `DISPATCH_UI` frame callback after mount items are dispatched. The Android `AnimationChoreographer` implementation only owns backend pause/resume state and conditionally forwards active frames to the shared backend.

Threading-wise:
- If invalidation happens before a frame starts, `mDestroyed` makes the frame no-op.
- If invalidation races with an already-running frame, `ReactChoreographer.removeFrameCallback` is serialized with callback execution via the `callbackQueues` monitor, so `onHostPause()` waits for the current `DISPATCH_UI` callback to finish before `unregister()` tears down the native binding.
- If the frame reposts itself in `schedule()`, the blocked removal observes and removes that callback before teardown continues.

Changelog:
[Android][Fixed] - Drive the shared animation backend from Fabric's frame callback during React instance teardown

Reviewed By: javache, zeyap

Differential Revision: D110321362

fbshipit-source-id: 77c462ee30aeec2d3af0dcd48b8eed15846ae5da
Summary:
Deprecates `DrawerLayoutAndroid` as part of the effort to keep core lean. It is Android only, and `react-native-drawer-layout` is a better, cross-platform alternative.

This adds a `deprecated` JSDoc tag to the component and its props (Flow and TypeScript) and a one-time runtime warning when the component is imported, pointing users to `react-native-drawer-layout`. No behavior changes.

## Changelog:

[ANDROID] [DEPRECATED] - Deprecate `DrawerLayoutAndroid`, use `react-native-drawer-layout` instead

Pull Request resolved: react#57397

Test Plan:
- `deprecated` shows a strikethrough on `DrawerLayoutAndroid` in editors.
- Importing/rendering `DrawerLayoutAndroid` logs the deprecation warning once, pointing to `react-native-drawer-layout`.

Reviewed By: javache

Differential Revision: D110334227

Pulled By: Abbondanzo

fbshipit-source-id: 40edd2a4e997ba3ab350c7b7229beb237e83a57b
Summary:
Pull Request resolved: react#57403

Make `RuntimeScheduler` implement `IEventLoopControl`.
Wire `ReactInstance` to register that scheduler on Hermes
runtimes that expose `ISetEventLoopControl`.
Clear the pointer during teardown before `RuntimeScheduler`
is destroyed.
Regenerate React Native C++ API snapshots for the public
header change.

Only the modern scheduler have the actual implementation, it's no-op
in the legacy scheduler.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D106744175

fbshipit-source-id: 90eb17ce8dbc929b5f2474e019de07ad28d9c21d
…7389)

Summary:
Pull Request resolved: react#57389

**Context**

Strict TypeScript API readiness: High quality inline docs should reach users via TypeScript in their IDEs.

**This diff**

Prior iterations of our Flow → TS translation stack dropped doc comments for default-exported values, and/or identifiers which change shape after type transformation (e.g. Flow `component` syntax), meaning many root APIs (`View`, `ScrollView`, `Pressable`, and others) showed no documentation on hover.

This diff extends the existing `reattachDocComments` transform to handle doc comment repositioning (suitable for the TS lang server) from a greater set of source positions:

- the exported declaration
- a `.displayName` assignment
- a HOC-wrapped inner component
- a renamed wrapper's public-named component
- a `declare const` / `declare export default typeof X` stub

**Impact**

(With the source code JSDoc improvements earlier in this stack.)

| Before (legacy types) | After (Strict API) |
| -- |
|  {F1991869487}  | {F1991869472}  |
| ⚠️ No inline docs for many symbols | ✅ New, detailed inline docs reach the TS server 🎉 |

Changelog:
[General][Fixed] - Preserve doc comments on root API symbols in the generated TypeScript types

Reviewed By: rubennorte

Differential Revision: D109316361

fbshipit-source-id: 8a83455fcf317f355bd7c5a67712d322248d8b00
Summary:
Removes long-deprecated `StatusBar` APIs that no longer do anything:

- Android: `backgroundColor` / `translucent` props and `setBackgroundColor()` / `setTranslucent()` (no effect under edge-to-edge, deprecated since 0.76).
- iOS: `networkActivityIndicatorVisible` prop and `setNetworkActivityIndicatorVisible()` (unsupported since iOS 13, deprecated since 0.77).

Also removes the now-unused native backing (`setColor`/`setTranslucent` Android, `setNetworkActivityIndicatorVisible` iOS, Android `DEFAULT_BACKGROUND_COLOR`).

See react#57384.

## Changelog:

[GENERAL] [BREAKING] - Remove deprecated `StatusBar` `backgroundColor` / `translucent` / `networkActivityIndicatorVisible` props and `setBackgroundColor` / `setTranslucent` / `setNetworkActivityIndicatorVisible` methods

Pull Request resolved: react#57392

Test Plan:
- `yarn flow check` — passes
- `yarn jest StatusBar` — passes
- `yarn test-typescript` — passes

Reviewed By: Abbondanzo, javache

Differential Revision: D110304943

Pulled By: cortinico

fbshipit-source-id: f7383c6bbbf8fd40042195ef7064b32692940499
@WoLewicki WoLewicki closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.