Skip to content

Fix idle momentum end on iOS (#58658) - #58658

Open
Abbondanzo wants to merge 2 commits into
react:mainfrom
Abbondanzo:export-D121477503
Open

Abbondanzo wants to merge 2 commits into
react:mainfrom
Abbondanzo:export-D121477503

Conversation

@Abbondanzo

@Abbondanzo Abbondanzo commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

Unmounting an idle ScrollView could incorrectly dispatch onMomentumScrollEnd because a non-tracking scroll view was treated as moving. Check UIScrollView.isDecelerating before the view leaves its window so the event is emitted only when momentum is active.

Add native regression coverage plus a shared RNTester Maestro flow. The flow runs on Android and iOS and verifies an idle unmount emits no momentum event, one fling emits exactly one begin/end pair, and a later unmount does not increment the count.

Changelog:
[iOS][Fixed] - Prevent idle ScrollViews from firing onMomentumScrollEnd during unmount

Differential Revision: D121477503

@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 Sep 23, 2026
@meta-codesync

meta-codesync Bot commented Sep 23, 2026

Copy link
Copy Markdown

@Abbondanzo has exported this pull request. If you are a Meta employee, you can view the originating Diff in D121477503.

@Abbondanzo

Copy link
Copy Markdown
Contributor Author

/review

@github-actions

Copy link
Copy Markdown

🤖 AI code review

Decision: Ready for human review (with comments)

Overall PR risk: Medium. The change modifies existing behavior in iOS ScrollView momentum-end emission on unmount and VirtualizedList onEndReached and onStartReached gating. The affected surface covers ScrollView and FlatList users, with narrow guards and direct rollback by revert. The kept finding shows the tester Scroll to start path never enables recording, so first use leaves output at running, while the broader plausible break is missed or extra momentum-end and edge callbacks.

🟡 Warning (1)

  • scrollToStart never enables event recording — packages/rn-tester/js/examples/FlatList/FlatList-onEndReached.js:99 (correctness) · id:a2b141266792
    Confidence: High — direct trace through the changed handlers.
    Impact if shipped: Medium — the tester button hangs on first use.
    Suggested remediation: Add recordEventsRef.current = true; at the start of scrollToStart, matching scrollToEnd.

    Evidence and reasoning

    scrollToEnd sets recordEventsRef to true. scrollToStart does not set it. All handlers return early when recordEventsRef is false. A user taps Scroll to start first. The list scrolls. The handlers ignore the events. The output stays at running. No later event reports it.


This review is advisory — it never blocks a merge and never auto-approves.

@meta-codesync meta-codesync Bot changed the title Fix idle momentum end on iOS Fix idle momentum end on iOS (#58658) Sep 24, 2026
Abbondanzo added a commit to Abbondanzo/react-native that referenced this pull request Sep 24, 2026
Summary:

Unmounting an idle `ScrollView` could incorrectly dispatch `onMomentumScrollEnd` because a non-tracking scroll view was treated as moving. Check `UIScrollView.isDecelerating` before the view leaves its window so the event is emitted only when momentum is active.

Add native regression coverage plus a shared RNTester Maestro flow. The flow runs on Android and iOS and verifies an idle unmount emits no momentum event, one fling emits exactly one begin/end pair, and a later unmount does not increment the count.

Changelog:
[iOS][Fixed] - Prevent idle `ScrollView`s from firing `onMomentumScrollEnd` during unmount

Differential Revision: D121477503
Abbondanzo added a commit to Abbondanzo/react-native that referenced this pull request Sep 24, 2026
Summary:

Unmounting an idle `ScrollView` could incorrectly dispatch `onMomentumScrollEnd` because a non-tracking scroll view was treated as moving. Check `UIScrollView.isDecelerating` before the view leaves its window so the event is emitted only when momentum is active.

Add native regression coverage plus a shared RNTester Maestro flow. The flow runs on Android and iOS and verifies an idle unmount emits no momentum event, one fling emits exactly one begin/end pair, and a later unmount does not increment the count.

Changelog:
[iOS][Fixed] - Prevent idle `ScrollView`s from firing `onMomentumScrollEnd` during unmount

Differential Revision: D121477503
Summary:
Programmatic list scrolling can move to an edge before asynchronously scheduled cells finish rendering. The scroll handler then skips the edge callback because the terminal cell is not yet in the render window, and no later scroll or layout event is guaranteed.

Recheck edge callbacks after post-scroll render updates. Track the item count when an edge callback is sent so internal measurement changes cannot emit duplicates while the list remains at the same edge; appending data or scrolling away re-arms the callback.

Changelog:
[General][Fixed] - Fire `VirtualizedList` edge callbacks after programmatic scrolling

Differential Revision: D100870075
Summary:

Unmounting an idle `ScrollView` could incorrectly dispatch `onMomentumScrollEnd` because a non-tracking scroll view was treated as moving. Check `UIScrollView.isDecelerating` before the view leaves its window so the event is emitted only when momentum is active.

Add native regression coverage plus a shared RNTester Maestro flow. The flow runs on Android and iOS and verifies an idle unmount emits no momentum event, one fling emits exactly one begin/end pair, and a later unmount does not increment the count.

Changelog:
[iOS][Fixed] - Prevent idle `ScrollView`s from firing `onMomentumScrollEnd` during unmount

Differential Revision: D121477503

This branch has not been deployed

No deployments
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. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant