Skip to content

Conversation

@danielroe
Copy link
Member

🔗 Linked issue

📚 Description

this handles an odd edge case I encountered.

const query = ref({ some_param: 1 })
const { execute } = await useFetch('https://jsonplaceholder.typicode.com/todos/1', {
  immediate: false,
  query,
})

query.value.some_param = 2
await execute()

Normally on client-side this works because reactivity has already created the new asyncData instance but on the server this can't be relied on.

@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Sep 28, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33341

nuxt

npm i https://pkg.pr.new/nuxt@33341

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33341

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33341

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33341

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33341

commit: e847e2b

@codspeed-hq
Copy link

codspeed-hq bot commented Sep 28, 2025

CodSpeed Performance Report

Merging #33341 will not alter performance

Comparing fix/immediate-change (e847e2b) with main (ce0fe6f)

Summary

✅ 10 untouched

@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

Walkthrough

Changes in packages/nuxt/src/app/composables/asyncData.ts introduce a lazy-initialisation wrapper createInitialFetch that returns a callable initialFetch used for the initial fetch path. initialFetch is created early and invoked where the prior inline initial-fetch logic existed. refresh now, when the per-key asyncData container is uninitialised (_init falsy), builds a fresh initialFetch via createInitialFetch and executes it; otherwise it calls the existing instance’s execute. execute is changed to delegate to asyncReturn.refresh. Public API signatures remain unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly describes the primary change, indicating a bug fix in Nuxt for handling non-immediate useAsyncData calls with a changing key during SSR. It succinctly summarises the core purpose of the PR and uses the conventional commit style. It is specific enough to convey the intended fix without extraneous detail.
Description Check ✅ Passed The description directly addresses the edge case under discussion and provides a clear code example illustrating the SSR failure scenario. It explains why the change is necessary and how it relates to the changeset. Although concise, it is clearly topical and informative.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/immediate-change

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 038a367 and e847e2b.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/composables/asyncData.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: codeql (actions)
  • GitHub Check: build
  • GitHub Check: code
🔇 Additional comments (2)
packages/nuxt/src/app/composables/asyncData.ts (2)

250-257: Correctly addresses previous review concerns.

The createInitialFetch wrapper properly handles lazy initialisation by:

  • Checking !nuxtApp._asyncData[key.value]?._init to reinitialise disabled instances (e.g. after _off())
  • Computing cached data for the current key.value to avoid stale data from previous keys

This fixes the edge case where non-immediate fetches with changing keys fail on SSR.

Based on learnings


395-402: Verify behaviour when refresh is called with explicit options on uninitialised asyncData.

When refresh(opts) is called and the asyncData is not initialised, the provided opts are ignored in favour of hardcoded { cause: 'initial', dedupe: options.dedupe } from the newly created initialFetch. This differs slightly from the previous review suggestion, which recommended passing opts through to execute.

Whilst using 'initial' cause for genuinely initial fetches is reasonable, confirm whether ignoring user-provided options like dedupe: 'defer' is acceptable in this scenario.


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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/nuxt/src/app/composables/asyncData.ts (1)

371-375: Optional: increment deps on lazy creation for parity with key‑watch path

When lazily creating a new per‑key instance here (SSR, immediate: false), consider bumping _deps like the key‑watcher does, to keep lifecycle semantics consistent.

       if (!nuxtApp._asyncData[key.value]?._init) {
         const cachedData = options.getCachedData!(key.value, nuxtApp, { cause: 'initial' })
         nuxtApp._asyncData[key.value] = createAsyncData(nuxtApp, key.value, _handler, options, cachedData)
+        nuxtApp._asyncData[key.value]!._deps++
       }

As per coding guidelines

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 107197b and 038a367.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/composables/asyncData.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: Tofandel
PR: nuxt/nuxt#33192
File: test/nuxt/use-async-data.test.ts:366-373
Timestamp: 2025-09-10T14:42:56.647Z
Learning: In the Nuxt useAsyncData test "should watch params deeply in a non synchronous way", the foo watcher intentionally updates both params.foo and params.locale using locale.value, simulating a scenario where one watcher consolidates multiple reactive values into a shared params object for testing debounced/non-synchronous behavior.

@danielroe danielroe merged commit c704151 into main Oct 6, 2025
83 of 84 checks passed
@danielroe danielroe deleted the fix/immediate-change branch October 6, 2025 13:18
This was referenced Oct 6, 2025
@github-actions github-actions bot mentioned this pull request Oct 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants