Skip to content

[router] Fix getState() returning stale state in state listeners - #51194

Merged
Ubax merged 3 commits into
expo:mainfrom
drakeo338:claude/51188-fix
Oct 12, 2026
Merged

Ubax merged 3 commits into
expo:mainfrom
drakeo338:claude/51188-fix

Conversation

@drakeo338

Copy link
Copy Markdown
Contributor

Why

Fixes #51188. Inside a state event listener, navigation.getState() returned the previous state while event.data.state held the new one.

How

The state emit effect ran before useNavigationHelpers, whose getState ref is refreshed in a passive effect when not on web or React Native (e.g. Jest). Effects run in declaration order, so listeners saw the old ref. The emit effect now follows useNavigationHelpers.

Test Plan

Added state-listener.test.ios.tsx, which compares getState() with event.data.state in a state listener after two navigations. It fails before the change and passes after (pnpm exec jest src/__tests__/state-listener in packages/expo-router).

Checklist

  • I added a changeset and followed this short guide
  • This diff will work correctly for npx expo prebuild & EAS Build (eg: updated a module plugin). Not applicable, no plugin change.
  • Conforms with the Documentation Writing Style Guide. Not applicable, no docs change.

@expo-bot expo-bot added the contributor: external PR author is not a member of the expo GitHub org label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Subscribed to pull request

File Patterns Mentions
packages/expo-router/** @Ubax, @hassankhan, @kitten

Generated by CodeMention

drakeo338 and others added 2 commits October 12, 2026 06:34
# Why

Fixes expo#51188. Inside a `state` event listener, `navigation.getState()`
returned the previous state while `event.data.state` held the new one.

# How

The `state` emit effect was declared before `useNavigationHelpers`. The
helpers' `getState` is backed by `useLatestCallback`, which refreshes its
ref in `useInsertionEffect` only when `document` is defined or
`navigator.product` is `ReactNative`, and otherwise in a passive effect
(the case in Jest). Effects run in declaration order, so listeners ran
before the ref was refreshed. The emit effect now follows
`useNavigationHelpers`.

# Test Plan

Added `state-listener.test.ios.tsx`, which compares `getState()` with
`event.data.state` in a `state` listener after two navigations. It fails
without the change and passes with it.
`pnpm exec jest src/__tests__/state-listener` in `packages/expo-router`.
@Ubax
Ubax merged commit 9858035 into expo:main Oct 12, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: external PR author is not a member of the expo GitHub org

Projects

None yet

Development

Successfully merging this pull request may close these issues.

expo-router 58: getState() in a state listener returns the previous state

3 participants