Files
bsky-social-app/patches/react-native@0.81.5.patch.md
T
Tomek Zawadzki 05bb19dd3f Patch ReactViewGroup to survive null child during subview clipping
Fixes the fatal Android crash "IllegalStateException: Required value was
null" in ReactViewGroup.updateSubviewClipStatus (Sentry APP-T20Q, 14 users
in 14 days on 1.128.0/1.129.0). Reentrant child removal during a clipping
pass can leave a null slot in allChildren below allChildrenCount; the
checkNotNull on that slot then kills the app during animated scrolls.
A null slot means the view is already detached, so skip it and count it
as clipped instead of throwing.

Unfixed upstream as of July 2026; the sibling fix attempt
facebook/react-native#57365 was abandoned. Verified against a
deterministic state-injection repro: unpatched RN crashes with the exact
Sentry stack (ReactViewGroup.kt:469/435/411), patched RN survives with
the app fully functional.

Note: takes effect on Android only when react-android is built from
source; see the caveat in the patch md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 11:15:48 +02:00

6.0 KiB

This second part of this patch is load bearing, do not remove.

RefreshControl Patch - iOS 17.4 Haptic Regression

Patching RCTRefreshControl.mm temporarily to play an impact haptic on refresh when using iOS 17.4 or higher. Since 17.4, there has been a regression somewhere causing haptics to not play on iOS on refresh. Should monitor for an update in the RN repo: https://github.com/facebook/react-native/issues/43388

RefreshControl Path - ScrollForwarder

Patching RCTRefreshControl.m and RCTRefreshControl.h to add a new forwarderBeginRefreshing method to the class. This method is used by ExpoScrollForwarder to initiate a refresh of the underlying UIScrollView from inside that module.

RCTPullToRefreshViewComponentView.mm Patch - RefreshControl initial props dropped on New Arch

TODO: Remove after bumping React Native to 0.82+ (fixed upstream by facebook/react-native#52615, #52584 and #53231).

On Fabric, updateProps diffs against _props, but the initial-layout replay in layoutSubviews passes _props as the new props too, so the diff is a no-op and tintColor/progressViewOffset/title are never applied on mount. This hides the pull-to-refresh spinner behind the floating home header (it stays at offset 0 instead of headerOffset). We diff against the oldProps argument instead, null-guarded with default props for the create-mutation path.

Issue: https://github.com/facebook/react-native/issues/56343

RCTEnhancedScrollView.mm / RCTScrollViewComponentView.mm Patch - centerContent insets stale after content resize on New Arch

TODO: Remove after bumping React Native to 0.87+ (fixed upstream by facebook/react-native#56832, commit d50c1b5207; first shipped in 0.87.0-rc.0).

On Fabric, centerContent centers by computing contentInset in centerContentIfNeeded, but that recompute only ran on setFrame/didAddSubview/scrollViewDidZoom - not when a state update assigns a new contentSize in updateState. Any content that resizes after mount inside a centerContent ScrollView (e.g. the lightbox image crop view getting its real aspect ratio from onLoad when the embed has no aspectRatio metadata) keeps the old insets: content rests off-center and the excess inset creates phantom scroll range, so the image can be dragged and parked off-center and the native scroll steals the swipe-down-to-dismiss pan. The old architecture paired every contentSize update with re-centering in RCTScrollView.updateContentSizeIfNeeded; Fabric dropped that link.

Backport of the upstream fix: setContentSize:/setCenterContent: overrides on RCTEnhancedScrollView that call centerContentIfNeeded, plus the updateProps guards so the contentInset prop does not fight the computed centering inset.

Issue: https://github.com/facebook/react-native/issues/55090

RCTScrollViewComponentView.mm Patch - ScrollView pinch/pan ignored outside content area on New Arch

TODO: Remove after bumping React Native to 0.87+ (fixed upstream by facebook/react-native#56747, commit efcab20908; first shipped in 0.87.0-rc.0).

On Fabric, betterHitTest in RCTScrollViewComponentView deliberately skips the _containerView and hit-tests its grandchildren, returning self (the wrapper component view) when the touch lands inside the scroll view bounds but outside any content. UIKit only delivers touches to a gesture recognizer when the hit view is the recognizer's view or a descendant of it, and the UIScrollView is a child of the wrapper - so its native pinch/pan recognizers never see those touches. In the lightbox this means pinch-to-zoom and pan-while-zoomed only respond when the fingers are over the image itself, not over the letterbox bars above/below it. On the old architecture, default UIKit hit-testing returns the UIScrollView itself for those touches, so everything works.

Backport of the upstream one-liner: return _scrollView instead of self so touches in the content-less area are attributed to the UIScrollView.

Issue: https://github.com/facebook/react-native/issues/54123 PR: https://github.com/react/react-native/pull/56747

ReactViewGroup.kt Patch - Fatal "Required value was null" during subview clipping on Android

Fixes Sentry issue APP-T20Q: IllegalStateException: Required value was null thrown by checkNotNull(allChildren?.get(idx)) in updateSubviewClipStatus, reached from ReactScrollView.onScrollChanged -> updateClippingRect during an animated smooth scroll (New Architecture, removeClippedSubviews).

The clipping loop in updateClippingToRect captures its bound once, but clipping a view (removeViewsInLayout) can synchronously trigger reentrant child removal (layout-change listeners, animation-end callbacks, Fabric mounting on the UI thread), which compacts allChildren and nulls the tail mid-loop. Upstream already catches the IndexOutOfBoundsException variant of this corruption with diagnostics, but the null-child variant throws IllegalStateException and escapes as a fatal crash. A null entry means the view is already detached, so we skip it and count it as clipped to keep index math aligned.

Not fixed upstream as of July 2026 (identical checkNotNull on main); the sibling fix attempt facebook/react-native#57365 for the same bookkeeping corruption (different stack) was abandoned. Re-check when bumping React Native.

Caveat: this hunk has no effect on the shipped app today. The Android build consumes the prebuilt com.facebook.react:react-android artifact from Maven Central, not the Kotlin sources in node_modules (unlike the iOS hunks above, which CocoaPods compiles from source). It only takes effect if we enable building React Native Android from source (includeBuild + dependency substitution in android/settings.gradle), or once an equivalent fix ships upstream. Kept so the fix is ready and documented either way.

RCTTextLayoutManager.mm Patch - Text overflows instead of wrapping on the last line

Issue: https://github.com/react/react-native/issues/53450#issuecomment-3298157830 Bandaid fix taken from: https://github.com/react/react-native/commit/581d643a9e59fd88f93757f80194e1efd11bd0e5