Skip to content

fix(android): keep the pager's Compose state out of the host's saved state - #1194

Open
abulenok wants to merge 1 commit into
callstack:mainfrom
abulenok:fix/android-compose-saved-state
Open

abulenok wants to merge 1 commit into
callstack:mainfrom
abulenok:fix/android-compose-saved-state

Conversation

@abulenok

@abulenok abulenok commented Oct 20, 2026 •

Copy link
Copy Markdown

Summary

Problem. Since 9.0, each PagerView on Android draws through a Jetpack Compose view inside the native view. Compose stores some state with rememberSaveable, including the pager's current page. That state goes into the host Fragment's saved-state store, keyed by the view's id. The pager's internal view has no id, so every pager in the app uses the same key (SaveableStateRegistry:-1).

When react-native-screens destroys the view of a screen that's covered by another, it saves that state. The next pager to mount under that key restores the old page and ignores initialPage. In our app, Home remounts its pager after you reorder feeds, so it opened on the old index and the tab bar didn't match the feed shown.

Solution. The library already gives the Compose view its own private Lifecycle owner so that react-native-screens can't kill it. The fix makes that same object provide the saved-state store too. The store starts empty and is never written out, so nothing is saved or restored. That's fine because React already owns the pager's state (initialPage and the page events).

Test Plan

What's required for testing (prerequisites)?

What are the steps to reproduce (after prerequisites)?

Compatibility

OS Implemented
iOS ❌
Android ✅

Checklist

  • I have tested this on a device and a simulator
  • I added the documentation in README.md
  • I updated the typed files (TS and Flow)

…state

The pager's internal ComposeView has no view id, so Compose saved its
rememberSaveable state (the pager's current page, the page hosts' view state)
in the host Fragment's SavedStateRegistry under the shared key
"SaveableStateRegistry:-1". When react-native-screens destroys the Fragment
view of a covered screen, that state is saved, and a pager mounted afterwards
restores it: it starts on the old page and ignores initialPage.

Give the ComposeView a SavedStateRegistryOwner from the same private owner that
already provides its Lifecycle. Its registry starts empty and is never saved,
so nothing is persisted or restored. React owns the pager state, so nothing is
lost. A unique view id would also avoid the collision, but every remount would
leave an orphaned entry in the Fragment's saved state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused change isolates saved state with correct lifecycle initialization and no identified blocking issues.

No open issues 🎉

What changed in this PR

Isolates Android pager Compose saved state from the host Fragment, preventing remounted pagers from restoring another pager’s page instead of using initialPage.

Changes:

  • Gives each pager’s existing lifecycle owner a private saved-state registry.
  • Initializes the registry without restored state and assigns it to the Compose view.
File Description
android/​src/​main/​java/​com/​reactnativepagerview/​ComposePagerView.kt Adds and connects a private saved-state registry while preserving lifecycle behavior.

Review effort: Balanced

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.

2 participants