Skip to content

fix(store): replace reactive Set/Map state on hydration instead of unioning it - #3162

Merged
posva merged 3 commits into
vuejs:v4from
winklemad:fix-hydrate-reactive-set-map-replace
Jul 17, 2026
Merged

posva merged 3 commits into
vuejs:v4from
winklemad:fix-hydrate-reactive-set-map-replace

Conversation

@winklemad

@winklemad winklemad commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3161.

Problem

When a setup store's state has a reactive(new Set([...])) / reactive(new Map([...])) with default entries, SSR hydration unions the defaults with the server state instead of replacing them, so entries the server removed survive (repro in #3161). The sibling ref() branch and HMR both replace; the server state is authoritative.

Cause

The reactive-hydration branch calls mergeReactiveObjects, whose Set/Map branches add server entries onto the client collection without clearing it first.

Fix

Clear a reactive Set/Map before merging in the hydration path only (store.ts), leaving $patch's own merge semantics (which also calls mergeReactiveObjects) unchanged.

Tests

Added a hydration test with non-empty reactive Set/Map defaults asserting the server state replaces them (the existing tests used empty defaults, so union == replace and the bug was invisible). It fails before this change and passes after; the full pinia suite stays green (211 passed), including $patch, and typecheck is clean.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed SSR hydration for reactive Set and Map values in setup stores.
    • Hydrated Set/Map contents now fully replace existing default entries, preventing stale items from lingering after hydration.
  • Tests
    • Added coverage to verify Set/Map hydration behavior for both top-level and nested reactive structures.

…ioning it

When a setup store's state contains a reactive(new Set()) / reactive(new Map())
with default entries, hydrating from server state unioned the two collections
instead of replacing them, so defaults the server had removed survived. The
sibling ref() branch and HMR both replace, and the server state is authoritative.

Clear the reactive Set/Map before merging in the hydration path only, leaving
$patch's own merge semantics unchanged. The existing hydration tests used empty
defaults, so they never exercised this.
@coderabbitai

coderabbitai Bot commented Jul 17, 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7bdf4206-054f-49da-b129-088888607d95

📥 Commits

Reviewing files that changed from the base of the PR and between 68eee06 and 058d59e.

📒 Files selected for processing (2)
  • packages/pinia/__tests__/state.spec.ts
  • packages/pinia/src/store.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/pinia/tests/state.spec.ts

📝 Walkthrough

Walkthrough

Setup-store hydration now clears reactive Set and Map values before merging server-provided state. Regression tests cover top-level collections and collections nested inside reactive objects.

Changes

Reactive collection hydration

Layer / File(s) Summary
Clear reactive collections during hydration
packages/pinia/src/store.ts, packages/pinia/__tests__/state.spec.ts
Reactive Set and Map targets are cleared before hydration merging, with tests confirming top-level and nested defaults are replaced.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • vuejs/pinia/3161 — Covers the same setup-store SSR hydration behavior for reactive Set and Map replacement.

Suggested reviewers: kazariex, posva, posva

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: SSR hydration now replaces reactive Set/Map state instead of unioning it.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@posva posva left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@posva
posva merged commit 5bdf26c into vuejs:v4 Jul 17, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from 🆕 Triaging to ✅ Done in Pinia Roadmap Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

Reactive Set/Map state is unioned instead of replaced during SSR hydration

2 participants