Skip to content

Blog / Route Transition Bug

State update after route transition

Sumeet Kumar Paul2 min read
The symptom
Data from the previous route appears briefly on the new one.
The cause
A route change does not cancel the request the old route started.
The fix
Tag each request and commit only if it is still the latest for the active route.

Problem

A component starts asynchronous work and then a route transition occurs before the work completes. When the async task resolves, it updates state that no longer matches the active route.

Under rapid navigation or slow network conditions, outdated async results can commit state after the user has moved to a different page, causing visual flickers or data appearing briefly in the wrong context.

  • Async request triggered on mount.
  • User navigates away quickly.
  • Async resolves after route change.
  • State update applies to outdated screen context.
page.tsx
srcapppracticestate-update-after-route-transitionpage.tsx
1useEffect(() => {
2  console.log("[BROKEN] request started for route:", route);
3
4  void (async () => {
5    const data = await fetchData(route);
6    console.log("[BROKEN] request resolved for route:", route);
7    setData(data);
8  })();
9}, [route]);

Because the async callback closes over the original route value, late responses may commit data that no longer matches the current navigation state.

Solution

Associate each route-triggered request with a request identity. Only allow the most recent route request to commit state.

Instead of assuming navigation invalidates previous async tasks, explicitly validate that the response still corresponds to the active route before updating state.

page.tsx
srcapppracticestate-update-after-route-transitionpage.tsx
1const requestIdRef = useRef(0);
2
3useEffect(() => {
4  const requestId = ++requestIdRef.current;
5  console.log("[FIXED] request started for route:", route);
6
7  void (async () => {
8    const data = await fetchData(route);
9
10    if (requestId !== requestIdRef.current) {
11      console.log("[FIXED] stale route response ignored");
12      return;
13    }
14
15    console.log("[FIXED] request committed for route:", route);
16    setData(data);
17  })();
18}, [route]);

By validating request identity against the current route, outdated responses are ignored and UI state remains aligned with active navigation context.

Check your own code

  1. 01Which requests are still running when the route changes?
  2. 02Is each response checked against the route that is active now?
  3. 03Have you switched routes quickly on a slow connection?
  • route transition bug
  • react navigation race
  • state leak
  • frontend lifecycle issue
  • ui correctness
View the source on GitHub ↗

Written by Sumeet Kumar Paul

Senior Frontend Engineer from India. Frontend Lab is where I reproduce real interface bugs and work through the fix.

Share article:

Need a hand?

I build frontends like this for a living.

Get in touch

More from the lab