Commit 3d51361
fix: prevent stale head() re-runs from polluting state
Problem:
When async loaders complete, we schedule a detached head() re-run to
execute head functions with fresh loaderData. However, if a new navigation
or invalidation starts before this re-run executes, it would update state
with stale data.
The previous location-based check only caught navigation to different
locations, but missed same-location invalidations (e.g., manual reload,
scheduled refetch).
Solution:
Implement a generation counter pattern to track load operations:
1. Add `router._loadGeneration` counter (excludes preloads)
2. Each non-preload `loadMatches()` call increments the counter
3. Store the generation in `inner.loadGeneration` when load starts
4. Before executing head re-runs or updates, check if generation matches
This detects ALL cases where a load has been superseded:
- Navigation to different location (new loadMatches call)
- Invalidation on same location (new loadMatches call)
- Any other scenario triggering a new load
The generation counter is a standard pattern in reactive systems (React,
RxJS) for detecting stale computations. Benefits:
- No circular references (vs storing full context)
- Minimal memory (4 bytes)
- Simple numeric comparison
- Clear semantics (higher = newer)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>1 parent 98f1319 commit 3d51361
2 files changed
+62
-6
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
38 | 64 | | |
39 | 65 | | |
40 | 66 | | |
| |||
584 | 610 | | |
585 | 611 | | |
586 | 612 | | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
587 | 616 | | |
588 | 617 | | |
589 | 618 | | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
590 | 622 | | |
591 | 623 | | |
592 | 624 | | |
593 | 625 | | |
594 | 626 | | |
595 | 627 | | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
596 | 632 | | |
597 | 633 | | |
598 | 634 | | |
| |||
901 | 937 | | |
902 | 938 | | |
903 | 939 | | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
904 | 946 | | |
905 | 947 | | |
906 | 948 | | |
| |||
959 | 1001 | | |
960 | 1002 | | |
961 | 1003 | | |
962 | | - | |
963 | | - | |
964 | | - | |
965 | 1004 | | |
966 | | - | |
967 | | - | |
968 | | - | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
969 | 1010 | | |
970 | 1011 | | |
971 | 1012 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
894 | 894 | | |
895 | 895 | | |
896 | 896 | | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
897 | 912 | | |
898 | 913 | | |
899 | 914 | | |
| |||
0 commit comments