Skip to content

fix(data-loaders): don't re-run instantly resolving parents when nested loaders attach - #2794

Open
lvyanyan wants to merge 1 commit into
vuejs:mainfrom
lvyanyan:fix/2684-nested-loaders-instant-parent
Open

fix(data-loaders): don't re-run instantly resolving parents when nested loaders attach#2794
lvyanyan wants to merge 1 commit into
vuejs:mainfrom
lvyanyan:fix/2684-nested-loaders-instant-parent

Conversation

@lvyanyan

@lvyanyan lvyanyan commented Aug 28, 2026

Copy link
Copy Markdown

fixes #2684

Context

In #2684, when all loaders are lazy and the parent loader resolves instantly (e.g. a synchronous cache), the parent loader ends up executed twice per navigation — and with a caching helper component the repeated executions can loop forever.

Root cause

Lazy loaders are executed at component setup, after the navigation has finished. With commit: 'after-load' (the default), a loader that resolves instantly commits immediately, nulling entry.pendingTo. When a nested loader later calls useParentLoader() inside its own query, the entry.pendingTo !== route check sees null and assumes the entry is stale, forcing a refetch (reload=true was hardcoded for nested calls) — running the parent loader a second time.

What this PR does

  • When the entry already committed data for the current route (entry.to === route and it has a pendingLoad), a nested useDataLoader() call reuses the resolved query instead of forcing a refetch.
  • An explicit reload() still forces nested loaders to re-run: the force flag travels through the loader context (new optional 4th slot) so nested calls inherit it.

Tests

Two new tests reproduce the issue with lazy loaders + instantly resolving parents (one for defineBasicLoader, one for defineColadaLoader with a cached parent). Both fail without the fix and pass with it. Full packages/router suite: 1689 passing.

Co-authored note: diagnosis guided by @posva's comment pinpointing the context reset timing.

Summary by CodeRabbit

  • New Features

    • Added forced reload support for data loaders.
    • Reloading a parent loader now also refreshes nested loaders.
  • Bug Fixes

    • Prevented loaders from running twice when data resolves immediately.
    • Fixed potential repeated loading loops with cached parent loaders.
    • Ensured each parent loader runs only once per navigation.

…ed loaders attach

Lazy loaders that resolve instantly get committed before nested loaders
run, nulling their pendingTo. When a nested loader then attached itself
(via useDataLoader), the pendingTo !== route check wrongly considered the
parent stale and forced a refetch, running the parent loader twice (and
potentially forever when combined with cached helpers).

- reuse the committed entry when it already has data for the current route
- propagate an explicit force flag (context 4th slot) so reload() still
  re-runs nested loaders for consistency

fixes vuejs#2684
@netlify

netlify Bot commented Aug 28, 2026

Copy link
Copy Markdown

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit f9b8b51
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/6a919d824b7ff40008c849ce

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a7ade10a-cb1d-4f82-b0e4-0689b1d5e3a0

📥 Commits

Reviewing files that changed from the base of the PR and between 0e91ac8 and f9b8b51.

📒 Files selected for processing (4)
  • packages/router/src/experimental/data-loaders/defineColadaLoader.ts
  • packages/router/src/experimental/data-loaders/defineLoader.spec.ts
  • packages/router/src/experimental/data-loaders/defineLoader.ts
  • packages/router/src/experimental/data-loaders/utils.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

defineLoader and defineColadaLoader now propagate forced reload state through nested loaders. They skip duplicate execution when route data is already committed. New tests cover lazy, basic, cached, and nested route navigation.

Changes

Nested loader execution

Layer / File(s) Summary
Force context propagation
packages/router/src/experimental/data-loaders/utils.ts, packages/router/src/experimental/data-loaders/defineLoader.ts, packages/router/src/experimental/data-loaders/defineColadaLoader.ts
The loader context and load functions now carry an optional force flag through nested loaders.
Route data guard and validation
packages/router/src/experimental/data-loaders/defineLoader.ts, packages/router/src/experimental/data-loaders/defineColadaLoader.ts, packages/router/src/experimental/data-loaders/defineLoader.spec.ts
Loaders skip duplicate execution when data exists for the current route. reload forces nested loaders to run. Tests cover lazy, basic, cached, and nested route navigation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to f9b8b

The change prevents unintended duplicate parent-loader execution while preserving explicit reload behavior; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Router
  participant ParentLoader
  participant ChildLoader
  Router->>ParentLoader: navigate to route
  ParentLoader->>ChildLoader: resolve nested loader context
  ChildLoader->>ChildLoader: check route data and force state
  ChildLoader-->>Router: complete navigation
Loading

Suggested reviewers: posva

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing repeated execution of instantly resolving parent data loaders when nested loaders attach.
Linked Issues check ✅ Passed The changes address issue #2684. They prevent duplicate or infinite parent-loader execution during navigation, preserve forced reload behavior, and add regression tests for basic and Colada loaders wi…
Out of Scope Changes check ✅ Passed All code and test changes relate to the linked issue and the stated objective of preventing duplicate nested-loader execution. No unrelated changes are evident.
Full details: Linked Issues check

Explanation

The changes address issue #2684. They prevent duplicate or infinite parent-loader execution during navigation, preserve forced reload behavior, and add regression tests for basic and Colada loaders with cached parents.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

Nested loader is called twice and has itself as a parent when parent loader resolves instantly

1 participant