Summary
Mixpanel.getGroup(groupKey, groupID) caches the returned MixpanelGroup instance in a single field this.group — not keyed by (groupKey, groupID). The first call seeds the cache with the first pair it sees; every subsequent call returns the same instance regardless of the requested key/id, so .set(...) / .setOnce(...) / .increment(...) etc. end up forwarded to whichever group was first cached.
Reproduction
const mixpanel = new Mixpanel(token, true);
await mixpanel.init();
mixpanel.identify("user-1");
mixpanel.getGroup("workspace_id", "A").set("$name", "Workspace A");
mixpanel.getGroup("workspace_id", "B").set("$name", "Workspace B");
mixpanel.getGroup("workspace_id", "C").set("$name", "Workspace C");
Expected
Three group profiles A, B, C each receive their own $name.
Actual
Workspace A's profile receives all three writes in sequence:
groupSetProperties("workspace_id", "A", {$name: "Workspace A"})
groupSetProperties("workspace_id", "A", {$name: "Workspace B"}) ← wrong group, overwrites
groupSetProperties("workspace_id", "A", {$name: "Workspace C"}) ← wrong group again
Workspaces B and C never receive a $name write. The native bridge gets the cached instance's frozen groupKey/groupID regardless of what the caller asked for.
Source
https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L371-L383
getGroup(groupKey, groupID) {
if (this.group) {
return this.group;
} else {
this.group = new MixpanelGroup(
this.token,
groupKey,
groupID,
this.mixpanelImpl
);
return this.group;
}
}
MixpanelGroup's constructor freezes the first (groupKey, groupID) (https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L807-L815) and .set() always forwards those frozen values to groupSetProperties (https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L825-L841).
Native SDKs do this correctly
Both native SDKs key the MixpanelGroup map by (groupKey, groupID):
- iOS:
MixpanelInstance.swift uses makeMapKey(groupKey:, groupID:) and looks up in a dictionary.
- Android:
MixpanelAPI.getGroup calls makeMapKey("<key>_<id>") and stores in mGroups: java.util.Map.
So the per-key caching exists in the native layer, but the JS shim short-circuits before reaching it.
Versions affected
Verified on both mixpanel-react-native@3.1.2 and mixpanel-react-native@3.3.0. Same cache shape on both.
Suggested fix
Key the JS-side cache by (groupKey, groupID):
getGroup(groupKey, groupID) {
const cacheKey = `${groupKey}_${JSON.stringify(groupID)}`;
if (!this.groupCache) this.groupCache = new Map();
let group = this.groupCache.get(cacheKey);
if (!group) {
group = new MixpanelGroup(this.token, groupKey, groupID, this.mixpanelImpl);
this.groupCache.set(cacheKey, group);
}
return group;
}
Current workaround
Instantiate MixpanelGroup directly, bypassing the cached shim:
import { MixpanelGroup } from "mixpanel-react-native";
const group = new MixpanelGroup(token, groupKey, groupID, mixpanel.mixpanelImpl);
group.set("$name", workspace.name);
This reaches into the private mixpanelImpl field — brittle but works.
Summary
Mixpanel.getGroup(groupKey, groupID)caches the returnedMixpanelGroupinstance in a single fieldthis.group— not keyed by(groupKey, groupID). The first call seeds the cache with the first pair it sees; every subsequent call returns the same instance regardless of the requested key/id, so.set(...)/.setOnce(...)/.increment(...)etc. end up forwarded to whichever group was first cached.Reproduction
Expected
Three group profiles
A,B,Ceach receive their own$name.Actual
Workspace
A's profile receives all three writes in sequence:groupSetProperties("workspace_id", "A", {$name: "Workspace A"})groupSetProperties("workspace_id", "A", {$name: "Workspace B"})← wrong group, overwritesgroupSetProperties("workspace_id", "A", {$name: "Workspace C"})← wrong group againWorkspaces
BandCnever receive a$namewrite. The native bridge gets the cached instance's frozengroupKey/groupIDregardless of what the caller asked for.Source
https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L371-L383
MixpanelGroup's constructor freezes the first(groupKey, groupID)(https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L807-L815) and.set()always forwards those frozen values togroupSetProperties(https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L825-L841).Native SDKs do this correctly
Both native SDKs key the MixpanelGroup map by
(groupKey, groupID):MixpanelInstance.swiftusesmakeMapKey(groupKey:, groupID:)and looks up in a dictionary.MixpanelAPI.getGroupcallsmakeMapKey("<key>_<id>")and stores inmGroups: java.util.Map.So the per-key caching exists in the native layer, but the JS shim short-circuits before reaching it.
Versions affected
Verified on both
mixpanel-react-native@3.1.2andmixpanel-react-native@3.3.0. Same cache shape on both.Suggested fix
Key the JS-side cache by
(groupKey, groupID):Current workaround
Instantiate
MixpanelGroupdirectly, bypassing the cached shim:This reaches into the private
mixpanelImplfield — brittle but works.