# Navigation state is properly managed at the root

- **Pattern:** `ab-001855` (`mobile-navigation-linking.navigation-structure.state-mgmt`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001855
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001855)

## Why it matters

Without root-level state tracking, analytics firing on screen changes, auth-gated redirects, and deep-link-driven UI toggles all become per-screen boilerplate that drifts out of sync. Missing `onStateChange` means you cannot detect when a user lands on a sensitive screen to refresh a token, or log funnel events centrally. The app ends up with duplicated tracking code and screens that never tell the rest of the app where the user is.

## Severity rationale

Medium because the app still navigates, but cross-screen analytics and global side effects become unreliable or duplicated.

## Remediation

Attach an `onStateChange` handler to `NavigationContainer` so analytics and global reactions fire in one place. Keep the latest state in a ref or store so any screen can read it:

```tsx
<NavigationContainer
  linking={linking}
  onStateChange={(state) => {
    const route = state?.routes[state.index]
    analytics.track('screen_view', { name: route?.name })
  }}
>
  <RootNavigator />
</NavigationContainer>
```

Put the handler in `src/navigation/index.tsx` so it ships with the container.

## Detection

- **ID:** `state-mgmt`
- **Severity:** `medium`
- **What to look for:** Check for root-level state management of navigation. Look for `useState` or external state libraries (Redux, Zustand) managing navigation state or app-wide conditions. Check whether navigation state is managed in `NavigationContainer`'s `onStateChange` callback.
- **Pass criteria:** Count all navigation state tracking mechanisms (onStateChange callbacks, state management subscriptions). At least 1 root-level state tracking mechanism must exist via `NavigationContainer.onStateChange` or a state management library. Any global navigation side effects (analytics, notifications) are handled at root level.
- **Fail criteria:** No state management for navigation. Navigation state is managed only locally in screens without global awareness. No more than 0 global navigation side effects should lack root-level coordination.
- **Skip (N/A) when:** Simple apps with fewer than 3 screens that have no cross-screen state requirements may skip this.
- **Detail on fail:** `"No navigation state management found at root level — changes in one screen don't update global state"` or `"onStateChange callback missing from NavigationContainer"`
- **Remediation:** Track navigation state at the root to enable analytics, logging, or state-driven conditionals:

  ```tsx
  const [navigationState, setNavigationState] = useState()

  <NavigationContainer
    linking={linking}
    onStateChange={state => {
      setNavigationState(state)
      // Log for analytics, etc.
    }}
  >
    {/* navigators */}
  </NavigationContainer>
  ```

---

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-001855
