Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork5.5k
feat(nuxt): add reset to default functionality foruseState andclearNuxtState#33527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Conversation
… management and reset functionality, introduce `_state` for internal use.
|
coderabbitaibot commentedOct 20, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
WalkthroughThis pull request refactors Nuxt's state composable by introducing Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (2)**/*.{ts,tsx,vue}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{ts,tsx,js,jsx,vue}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
🧠 Learnings (2)📚 Learning: 2024-12-12T12:36:34.871ZApplied to files:
📚 Learning: 2025-09-10T14:42:56.647ZApplied to files:
🔇 Additional comments (2)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for usingCodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/nuxt/src/app/composables/state.ts (1)
65-76:Safer key derivation and leaner payload clearing
_allKeysshould only include$s-prefixed entries; otherwise non‑useState keys (if any) would be transformed and re‑prefixed, causing unintended clears.- When removing without reset, prefer deleting the key to avoid retaining enumerable
undefinedslots (keeps payload slim and prevents stale keys from reappearing in subsequent clears).Apply:
- const _allKeys = Object.keys(nuxtApp.payload.state)- .map(key => key.substring(useStateKeyPrefix.length))+ const _allKeys = Object.keys(nuxtApp.payload.state)+ .filter(key => key.startsWith(useStateKeyPrefix))+ .map(key => key.substring(useStateKeyPrefix.length)) for (const _key of _keys) {- clearNuxtStateByKey(nuxtApp, useStateKeyPrefix + _key, reset ?? true)+ clearNuxtStateByKey(nuxtApp, useStateKeyPrefix + _key, reset ?? true) }And streamline per‑key clearing:
-function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {- if (key in nuxtApp.payload.state) {- nuxtApp.payload.state[key] = undefined- }-- if (nuxtApp._state[key]) {- nuxtApp._state[key]!.data.value = reset ? unref(nuxtApp._state[key]!._default()) : undefined- }-}+function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {+ if (reset) {+ if (nuxtApp._state[key]) {+ // Resets both _state data and payload via toRef linkage+ nuxtApp._state[key]!.data.value = unref(nuxtApp._state[key]!._default())+ return+ }+ // No _state registered: remove from payload if present+ delete nuxtApp.payload.state[key]+ } else {+ // Remove from payload and null out internal value if tracked+ delete nuxtApp.payload.state[key]+ if (nuxtApp._state[key]) {+ nuxtApp._state[key]!.data.value = undefined+ }+ }+}
🧹 Nitpick comments (2)
packages/nuxt/src/app/nuxt.ts (1)
145-151:Internal _state map looks good; consider GC of stale keysThe addition is well‑typed and initialised correctly. To avoid long‑lived growth on the client, consider pruning entries (e.g. delete nuxtApp._state[key]) when a state is cleared without reset and no watchers remain. This keeps memory proportional to active state keys.
Also applies to: 319-319
packages/nuxt/src/app/composables/state.ts (1)
45-56:You can simplify to a plain ref; computed indirection is unnecessaryGiven
_state[key]andstateare established above, returningstate as Ref<T>is sufficient. The computed fallback tonuxtApp.payload.state[key]is dead code in this path and adds overhead.Apply:
- return computed({- get () {- return nuxtApp._state[key]?.data.value ?? nuxtApp.payload.state[key]- },- set (value) {- if (nuxtApp._state[key]) {- nuxtApp._state[key]!.data.value = value- } else {- nuxtApp.payload.state[key] = value- }- },- })+ return state as Ref<T>If you keep the computed, consider at least collapsing the branches to
state.valuefor both get/set. As per coding guidelines.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/nuxt/src/app/composables/state.ts(2 hunks)packages/nuxt/src/app/nuxt.ts(2 hunks)test/nuxt/composables.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Follow standard TypeScript conventions and best practices
Files:
packages/nuxt/src/app/nuxt.tstest/nuxt/composables.test.tspackages/nuxt/src/app/composables/state.ts
**/*.{test,spec}.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Write unit tests for core functionality using
vitest
Files:
test/nuxt/composables.test.ts
🧬 Code graph analysis (2)
test/nuxt/composables.test.ts (2)
packages/nuxt/src/app/composables/state.ts (2)
clearNuxtState(60-77)useState(20-57)packages/nuxt/src/app/nuxt.ts (1)
useNuxtApp(557-570)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
useNuxtApp(557-570)NuxtApp(206-206)
🔇 Additional comments (1)
test/nuxt/composables.test.ts (1)
203-207:Test coverage aligns with intended semanticsThe scenarios comprehensively cover payload registration, unwrapped defaults, shared-key behaviour, and reset/remove paths. These rely on useState unref’ing the init return on initialisation; see my comment in state.ts proposing that fix.
Also applies to: 212-216, 217-224, 225-234, 243-285, 286-344
Uh oh!
There was an error while loading.Please reload this page.
codspeed-hqbot commentedOct 20, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
CodSpeed Performance ReportMerging#33527 willdegrade performances by 13.81%Comparing Summary
Benchmarks breakdown
|
pkg-pr-newbot commentedOct 20, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@nuxt/kit@nuxt/nitro-servernuxt@nuxt/rspack-builder@nuxt/schema@nuxt/vite-builder@nuxt/webpack-buildercommit: |
Harm-Nullix commentedOct 20, 2025
Quick example addition: |
huang-julien left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think that it can be really confusing for users and quite hard to debug if there's multipleuseState that retuns differents default values
Harm-Nullix commentedOct 22, 2025
I agree, E.g. You can tackle this by creating a composable around your I think it is a valid point about data storage and the keys that are being used, I've come across this myself while useing |
Harm-Nullix commentedOct 29, 2025
So what is next?
|
huang-julien commentedOct 29, 2025
Yes that's just how it currently works with useAsyncData. |
| constuseStateKeyPrefix='$s' | ||
| constgetDefault=()=>undefined | ||
| typeInitOption<T>=(()=>T|Ref<T>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
You can useMaybeRefOrGetter instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Nice one, added
| returncomputed({ | ||
| get(){ | ||
| returnnuxtApp._state[key]?.data.value??nuxtApp.payload.state[key] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We probably don't want to have two possible reference for a state 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Agreed, I've updated that to the state ref
| if(keyinnuxtApp.payload.state){ | ||
| nuxtApp.payload.state[key]=undefined | ||
| } | ||
| clearNuxtStateByKey(nuxtApp,useStateKeyPrefix+_key,reset??true) |
huang-julienOct 29, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Let's avoid breaking changes. We can make it true by default for v5 though by using a future flag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Agreed, toggled the flag to be false bd
Harm-NullixNov 10, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
@huang-julien , is this something I can add to project as a future change, lets say as experimental?
It feels "intrusive" to do so in the Nuxt project.
…eset logic and type improvements (review fixes)
… parameter and its behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
packages/nuxt/src/app/composables/state.ts (5)
9-10:Simplify type alias or rename to avoid confusion.
UseStateOptions<T>suggests an options object with multiple properties, but it's merely an alias forInitOption<T>(a function type). This naming can mislead users and maintainers.Consider either:
- Using
InitOption<T>directly throughout, or- Renaming to
UseStateInit<T>to reflect that it's a single init function rather than a structured options object.Apply this diff to use
InitOption<T>directly:-type InitOption<T> = (() => MaybeRefOrGetter<T>)-type UseStateOptions<T> = InitOption<T>+type InitOption<T> = () => MaybeRefOrGetter<T>Then replace
UseStateOptions<T>withInitOption<T>on lines 18, 19, and 25.
21-24:Clarify the autoKey handling logic.The autoKey extraction and re‑insertion logic is difficult to follow. The code pops the last argument if it's a string, then conditionally unshifts it back if
args[0]is not a string. This approach is fragile and not immediately clear to maintainers.Consider refactoring for clarity, or at minimum add inline comments explaining the intended argument patterns (e.g.,
(key, init),(init),(key), etc.) and how this logic resolves them.
45-45:Type assertion bypasses overload return‑type differences.The
as Ref<T>assertion reconciles two overloads that declare different return types (Ref<UnwrapRef<T>>vsRef<T>). Whilst the runtime behaviour is correct (values are unwrapped viatoValue), this assertion may obscure type‑checking issues and could lead to subtle inference problems for callers.If the inconsistency (flagged earlier) is intentional, consider documenting why the types differ; otherwise, unify the return types to eliminate the need for this assertion.
69-77:Potential redundancy and intermediate state flicker when resetting.When
resetis true andnuxtApp._state[key]exists, the code performs two assignments:
- Line 71 sets
nuxtApp.payload.state[key] = undefined(directly on the object)- Line 75 sets
nuxtApp._state[key]!.data.value = toValue(...)(through the ref)Since
_state[key].datais atoReftopayload.state[key](created on line 36/38), they reference the same underlying value. The assignment on line 71 may cause an intermediateundefinedstate before line 75 overwrites it with the default, potentially triggering watchers or causing a visual flicker.Consider skipping the line 71 assignment when
_state[key]exists andresetis true, or reordering to set the reset value first.Apply this diff to avoid the intermediate state:
function clearNuxtStateByKey (nuxtApp: NuxtApp, key: string, reset: boolean): void {- if (key in nuxtApp.payload.state) {- nuxtApp.payload.state[key] = undefined- }- if (nuxtApp._state[key]) { nuxtApp._state[key]!.data.value = reset ? toValue(nuxtApp._state[key]!._default) : undefined+ } else if (key in nuxtApp.payload.state) {+ // Clear legacy state entries that lack _state registry+ nuxtApp.payload.state[key] = undefined } }
41-43:Consider error handling for init function execution.The code calls
toValue(defaultFn)(line 42) without catching potential errors. If the init function throws during initial hydration or reset, the exception will propagate and could break the component or app.Consider wrapping the
toValuecall (here and on line 75) in a try‑catch to provide a more helpful error message or graceful fallback.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/nuxt/src/app/composables/state.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/state.ts
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
useNuxtApp(557-570)NuxtApp(206-206)
⏰ 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). (2)
- GitHub Check: code
- GitHub Check: build
🔇 Additional comments (4)
packages/nuxt/src/app/composables/state.ts (4)
1-3:LGTM! Imports support new functionality.The added imports (
MaybeRefOrGetter,UnwrapRef,toValue, andNuxtApptype) are all used appropriately throughout the refactored code.
18-19:Verify the return‑type inconsistency between overloads.The first overload returns
Ref<UnwrapRef<T>>whilst the second returnsRef<T>. Since both code paths usetoValue(defaultFn)(line 42) to unwrapMaybeRefOrGetter<T>, both should exhibit the same unwrapping behaviour at runtime. This type mismatch may cause TypeScript inference issues when the key is omitted or auto-generated.Confirm whether both overloads should return
Ref<UnwrapRef<T>>for consistency, or clarify why they differ.
49-67:LGTM!clearNuxtStatecorrectly delegates to per‑key helper.The addition of the
resetparameter and delegation toclearNuxtStateByKeymaintains clarity. Defaultingresettofalsepreserves existing behaviour for callers who don't opt in to the new reset feature.
37-40:No action required—_stateis properly defined and initialised.Verification confirms that
_stateis correctly defined in the_NuxtAppinterface (line 146–150, packages/nuxt/src/app/nuxt.ts) with the expected structure:Record<string, { data: Ref<unknown>, _default: () => MaybeRefOrGetter<unknown> } | undefined>. It is initialised increateNuxtApp(line 319) asshallowReactive({}), and the code at lines 37–40 correctly assigns the expected structure.
4cf927d to8815072CompareHarm-Nullix commentedNov 18, 2025
@huang-julien , I think I have done everything needed to make this: Please let me know if anything is needed on my side to move things along. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
docs/4.api/3.utils/clear-nuxt-state.md (1)
12-14:Improve sentence structure for clarity.The phrase "initial value passing a second parameter" is grammatically awkward. Consider adding a comma or rephrasing for better readability.
Apply this diff to improve the grammar:
-You can also reset the state to the initial value passing a second parameter+You can also reset the state to the initial value by passing a second parameterpackages/nuxt/src/app/composables/state.ts (2)
25-25:Consider stronger type safety for the destructuring.The type assertion
as [unknown, InitOption<T>]bypasses TypeScript's type checking. Given the complex autoKey logic above, this could mask runtime errors ifargsdoesn't have the expected shape.Consider either:
- Simplifying the argument handling logic to make the types provable, or
- Adding runtime validation to ensure
argsmatches the expected structure before destructuring.
82-89:Consider using Nuxt's logger instead of console.error.The
console.erroron line 86 logs errors directly to the console. For consistency with Nuxt's logging patterns and better control in production environments, consider using a Nuxt logger utility if available.This is a minor suggestion since the error case (init function throwing) should be rare.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/4.api/3.utils/clear-nuxt-state.md(1 hunks)packages/nuxt/src/app/composables/state.ts(2 hunks)packages/nuxt/src/app/nuxt.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/nuxt/src/app/nuxt.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/nuxt/src/app/composables/state.ts (1)
packages/nuxt/src/app/nuxt.ts (2)
useNuxtApp(559-572)NuxtApp(208-208)
🪛 LanguageTool
docs/4.api/3.utils/clear-nuxt-state.md
[uncategorized] ~13-~13: Possible missing comma found.
Context: ...can also reset the state to the initial value passing a second parameter : ## Type ...
(AI_HYDRA_LEO_MISSING_COMMA)
[uncategorized] ~24-~24: Loose punctuation mark.
Context: ...lean): void ``` ## Parameters -keys: One or an array of keys that are used i...
(UNLIKELY_OPENING_PUNCTUATION)
[uncategorized] ~25-~25: Loose punctuation mark.
Context: ...l state** will be invalidated. -reset: Reset the state to the initial value pa...
(UNLIKELY_OPENING_PUNCTUATION)
⏰ 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). (2)
- GitHub Check: code
- GitHub Check: docs
🔇 Additional comments (5)
packages/nuxt/src/app/composables/state.ts (5)
1-10:LGTM! Clean type definitions and imports.The import updates align with Vue 3 best practices, and the new type definitions (
InitOption<T>,UseStateReturn<T>) clearly express the API surface. ThegetDefaulthelper is straightforward.
33-43:LGTM! State initialisation logic is solid.The internal
_statetracking and deferred initialisation usingtoValueWithFallbackcorrectly handle both the initial setup and future reset scenarios. The previous concern about nested refs has been addressed.
49-67:LGTM! Clear and backward-compatible API extension.The
resetparameter is properly defaulted tofalse, maintaining backward compatibility whilst enabling the new reset-to-init behaviour. The delegation toclearNuxtStateByKeykeeps the logic clean.
69-76:LGTM! Proper handling of both tracked and legacy state.The function correctly handles both
_state-tracked entries and legacy entries without registry, ensuring smooth backward compatibility and migration paths.
21-24:Remove the unused autoKey logic—it contradicts the type signatures and is never executed.Based on verification across the codebase, the
autoKeyextraction (lines 21–24) is dead code. The type signatures guarantee that the last argument is either anInitOption<T>(a function) or undefined, never a string. All 30+ real-world calls follow the documented overload patterns (useState('key', () => init)oruseState(() => init)), and none pass a string as the final parameter.Delete lines 21–24 and update the implementation to directly destructure args:
exportfunctionuseState<T>(...args:any):UseStateReturn<T>{const[_key,init]=args.length===1&&typeofargs[0]!=='string' ?[undefined,args[0]] :argsas[unknown,InitOption<T>]Then throw the existing error if
_keyis missing, which will handle theuseState(init)case properly.
useStateandclearNuxtStateto improve statemanagement and reset functionality, introduce_statefor internal use.🔗 Linked issue
#32117
📚 Description
The setup for this PR is a starting point with a proposed solution
It does break the current logic as it does not automatically sets the state to undefined but resets it to the given
initfunctionOther paths considered:
initparameter aoptionsobject (fallback to oldinitparam),initvalueclearNuxtStateto keep current logic as is.nuxtApp._stateuseStateless clearuseAsyncDataTODO
When a solid path is determined, I will add guiding docs to the current functionality