Skip to content

fix(combineSlices): scope stateProxyMap per instance to prevent cross-instance proxy collisions#5319

Merged
EskiMojo14 merged 1 commit into
reduxjs:masterfrom
JSap0914:fix/combineSlices-per-instance-stateProxyMap
Jun 23, 2026
Merged

fix(combineSlices): scope stateProxyMap per instance to prevent cross-instance proxy collisions#5319
EskiMojo14 merged 1 commit into
reduxjs:masterfrom
JSap0914:fix/combineSlices-per-instance-stateProxyMap

Conversation

@JSap0914

Copy link
Copy Markdown
Contributor

Problem

combineSlices.ts declares stateProxyMap at module level:

const stateProxyMap = new WeakMap<object, object>()

This WeakMap is shared across all combineSlices() instances. The module-level noopReducer always returns the same emptyObject constant as initial state when no slices are given, so two empty combineSlices() calls both produce the same object reference as their initial state.

When a selector from instance A is called first with that shared state, a proxy is cached in the module-level stateProxyMap keyed by the state reference. A subsequent selector call from instance B with the same state reference retrieves the cached proxy from instance A, which closes over instance A's reducerMap and initialStateCache — so instance B's injected reducer's initial value is never consulted and the wrong value is returned.

const reducer1 = combineSlices()
const reducer2 = combineSlices()

// Both return the same  reference (noopReducer)
const preState = reducer1(undefined, { type: '@@INIT' })

reducer1.inject({ reducerPath: 'x', reducer: () => 'A' })
reducer2.inject({ reducerPath: 'x', reducer: () => 'B' })

const sel1 = reducer1.selector(s => s.x)
const sel2 = reducer2.selector(s => s.x)

sel1(preState) // 'A'  ✓
sel2(preState) // 'A'  ✗  — should be 'B'

Fix

Move stateProxyMap inside combineSlices (one WeakMap per instance) and thread it as an explicit parameter through createStateProxy. Each instance now has an isolated proxy cache, so selecting against a shared state object from a different instance creates a fresh proxy with the correct reducerMap and initialStateCache.

Testing

A new unit test in combineSlices.test.ts reproduces the bug (fails without this fix, passes with it). All 88 existing test files continue to pass.

AI-assisted fix

…-instance proxy collisions

Two separate combineSlices() instances both sharing the same emptyObject
reference as their initial state (from the module-level noopReducer) would
have their selectors use the wrong proxy when the same state object was
passed to both.  The module-level stateProxyMap WeakMap meant the second
selector call got the cached proxy from the first call, carrying the first
instance's reducerMap and initialStateCache.

Fix: move stateProxyMap inside combineSlices so each instance owns its own
proxy cache.  Add stateProxyMap as an explicit parameter to createStateProxy
so the call site passes the per-instance map.

> AI-assisted fix (GitHub Copilot / Claude)
Copilot AI review requested due to automatic review settings June 23, 2026 11:17
@codesandbox

codesandbox Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@netlify

netlify Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploy Preview for redux-starter-kit-docs ready!

Name Link
🔨 Latest commit d5b5136
🔍 Latest deploy log https://app.netlify.com/projects/redux-starter-kit-docs/deploys/6a3a6b5c2f43840009c02d8d
😎 Deploy Preview https://deploy-preview-5319--redux-starter-kit-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

@reduxjs/rtk-codemods

npm i https://pkg.pr.new/@reduxjs/rtk-codemods@d5b5136 -D
yarn add https://pkg.pr.new/@reduxjs/rtk-codemods@d5b5136.tgz -D
pnpm add https://pkg.pr.new/@reduxjs/rtk-codemods@d5b5136.tgz -D
bun add https://pkg.pr.new/@reduxjs/rtk-codemods@d5b5136.tgz -D

@rtk-query/codegen-openapi

npm i https://pkg.pr.new/@rtk-query/codegen-openapi@d5b5136 -D
yarn add https://pkg.pr.new/@rtk-query/codegen-openapi@d5b5136.tgz -D
pnpm add https://pkg.pr.new/@rtk-query/codegen-openapi@d5b5136.tgz -D
bun add https://pkg.pr.new/@rtk-query/codegen-openapi@d5b5136.tgz -D

@rtk-query/graphql-request-base-query

npm i https://pkg.pr.new/@rtk-query/graphql-request-base-query@d5b5136 -D
yarn add https://pkg.pr.new/@rtk-query/graphql-request-base-query@d5b5136.tgz -D
pnpm add https://pkg.pr.new/@rtk-query/graphql-request-base-query@d5b5136.tgz -D
bun add https://pkg.pr.new/@rtk-query/graphql-request-base-query@d5b5136.tgz -D

@reduxjs/toolkit

npm i https://pkg.pr.new/@reduxjs/toolkit@d5b5136 -D
yarn add https://pkg.pr.new/@reduxjs/toolkit@d5b5136.tgz -D
pnpm add https://pkg.pr.new/@reduxjs/toolkit@d5b5136.tgz -D
bun add https://pkg.pr.new/@reduxjs/toolkit@d5b5136.tgz -D

commit: d5b5136

@EskiMojo14

Copy link
Copy Markdown
Collaborator

seems edge case-y, but nonetheless a legitimate bug with a simple fix - thanks! (and thank you for disclosing that you're using AI for these, the honesty is appreciated :P)

@EskiMojo14 EskiMojo14 merged commit 62d21b0 into reduxjs:master Jun 23, 2026
48 checks passed
aryaemami59 added a commit to aryaemami59/redux-toolkit that referenced this pull request Jun 29, 2026
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.

3 participants