mixpanel-react-native 3.1.2 → 3.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +12 -1
- package/.github/dependabot.yml +7 -0
- package/.github/workflows/node.js.yml +24 -1
- package/.vscode/settings.json +2 -1
- package/MixpanelReactNative.podspec +1 -1
- package/Samples/MixpanelExample/ios/MixpanelExample.xcworkspace/contents.xcworkspacedata +10 -0
- package/Samples/MixpanelExample/ios/Podfile.lock +1996 -0
- package/Samples/MixpanelStarter/.bundle/config +2 -0
- package/Samples/MixpanelStarter/.env.example +4 -0
- package/Samples/MixpanelStarter/.eslintrc.js +4 -0
- package/Samples/MixpanelStarter/.prettierrc.js +5 -0
- package/Samples/MixpanelStarter/.watchmanconfig +1 -0
- package/Samples/MixpanelStarter/App.tsx +10 -0
- package/Samples/MixpanelStarter/CLAUDE.md +538 -0
- package/Samples/MixpanelStarter/Gemfile +16 -0
- package/Samples/MixpanelStarter/INTEGRATION_GUIDE.md +606 -0
- package/Samples/MixpanelStarter/README.md +406 -0
- package/Samples/MixpanelStarter/__tests__/MixpanelContext.test.tsx +63 -0
- package/Samples/MixpanelStarter/android/app/build.gradle +119 -0
- package/Samples/MixpanelStarter/android/app/debug.keystore +0 -0
- package/Samples/MixpanelStarter/android/app/proguard-rules.pro +10 -0
- package/Samples/MixpanelStarter/android/app/src/main/AndroidManifest.xml +27 -0
- package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainActivity.kt +22 -0
- package/Samples/MixpanelStarter/android/app/src/main/java/com/mixpanelstarter/MainApplication.kt +27 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/values/strings.xml +3 -0
- package/Samples/MixpanelStarter/android/app/src/main/res/values/styles.xml +9 -0
- package/Samples/MixpanelStarter/android/build.gradle +21 -0
- package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/Samples/MixpanelStarter/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/Samples/MixpanelStarter/android/gradle.properties +44 -0
- package/Samples/MixpanelStarter/android/gradlew +251 -0
- package/Samples/MixpanelStarter/android/gradlew.bat +99 -0
- package/Samples/MixpanelStarter/android/settings.gradle +6 -0
- package/Samples/MixpanelStarter/app.json +4 -0
- package/Samples/MixpanelStarter/babel.config.js +14 -0
- package/Samples/MixpanelStarter/index.js +9 -0
- package/Samples/MixpanelStarter/ios/.xcode.env +11 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/AppDelegate.swift +48 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/Images.xcassets/Contents.json +6 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/Info.plist +55 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/LaunchScreen.storyboard +47 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter/PrivacyInfo.xcprivacy +38 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/project.pbxproj +482 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter.xcodeproj/xcshareddata/xcschemes/MixpanelStarter.xcscheme +88 -0
- package/Samples/MixpanelStarter/ios/MixpanelStarter.xcworkspace/contents.xcworkspacedata +10 -0
- package/Samples/MixpanelStarter/ios/Podfile +34 -0
- package/Samples/MixpanelStarter/ios/Podfile.lock +2839 -0
- package/Samples/MixpanelStarter/jest.config.js +3 -0
- package/Samples/MixpanelStarter/metro.config.js +42 -0
- package/Samples/MixpanelStarter/package-lock.json +12141 -0
- package/Samples/MixpanelStarter/package.json +51 -0
- package/Samples/MixpanelStarter/src/@types/env.d.ts +3 -0
- package/Samples/MixpanelStarter/src/App.tsx +83 -0
- package/Samples/MixpanelStarter/src/components/ActionButton.tsx +92 -0
- package/Samples/MixpanelStarter/src/components/ErrorBoundary.tsx +81 -0
- package/Samples/MixpanelStarter/src/components/EventTrackingLog.tsx +163 -0
- package/Samples/MixpanelStarter/src/components/FlagCard.tsx +199 -0
- package/Samples/MixpanelStarter/src/components/InfoCard.tsx +77 -0
- package/Samples/MixpanelStarter/src/components/TestResultDisplay.tsx +181 -0
- package/Samples/MixpanelStarter/src/constants/tracking.ts +77 -0
- package/Samples/MixpanelStarter/src/contexts/MixpanelContext.tsx +159 -0
- package/Samples/MixpanelStarter/src/screens/FeatureFlagsScreen.tsx +1011 -0
- package/Samples/MixpanelStarter/src/screens/HomeScreen.tsx +307 -0
- package/Samples/MixpanelStarter/src/screens/OnboardingScreen.tsx +253 -0
- package/Samples/MixpanelStarter/src/screens/SettingsScreen.tsx +316 -0
- package/Samples/MixpanelStarter/src/types/flags.types.ts +42 -0
- package/Samples/MixpanelStarter/src/types/mixpanel.types.ts +26 -0
- package/Samples/MixpanelStarter/tsconfig.json +13 -0
- package/__tests__/flags.test.js +730 -0
- package/__tests__/index.test.js +7 -3
- package/__tests__/jest_setup.js +18 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/mixpanel/reactnative/MixpanelReactNativeModule.java +272 -2
- package/index.d.ts +64 -1
- package/index.js +42 -3
- package/ios/MixpanelReactNative.m +19 -1
- package/ios/MixpanelReactNative.swift +183 -5
- package/javascript/mixpanel-flags-js.js +463 -0
- package/javascript/mixpanel-flags.js +290 -0
- package/javascript/mixpanel-main.js +13 -1
- package/package.json +2 -2
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
# Integration Guide: Adding Mixpanel to Your React Native App
|
|
2
|
+
|
|
3
|
+
This guide provides step-by-step instructions for integrating Mixpanel into your React Native application using the patterns demonstrated in MixpanelStarter.
|
|
4
|
+
|
|
5
|
+
## Prerequisites Checklist
|
|
6
|
+
|
|
7
|
+
Before you begin, ensure you have:
|
|
8
|
+
|
|
9
|
+
- [ ] React Native app (0.70+) with TypeScript
|
|
10
|
+
- [ ] Mixpanel project token ([create one](https://mixpanel.com/register))
|
|
11
|
+
- [ ] `@react-native-async-storage/async-storage` installed (required dependency)
|
|
12
|
+
|
|
13
|
+
## 8-Step Integration Process
|
|
14
|
+
|
|
15
|
+
### Step 1: Install Dependencies
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install mixpanel-react-native @react-native-async-storage/async-storage
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For iOS, install pods:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd ios && pod install && cd ..
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Step 2: Set Up Environment Variables (Optional but Recommended)
|
|
28
|
+
|
|
29
|
+
Install dotenv support:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install react-native-dotenv --save-dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Configure `babel.config.js`:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
module.exports = {
|
|
39
|
+
presets: ['module:@react-native/babel-preset'],
|
|
40
|
+
plugins: [
|
|
41
|
+
[
|
|
42
|
+
'module:react-native-dotenv',
|
|
43
|
+
{
|
|
44
|
+
moduleName: '@env',
|
|
45
|
+
path: '.env',
|
|
46
|
+
safe: false,
|
|
47
|
+
allowUndefined: true,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Create `.env`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
MIXPANEL_TOKEN=your_token_here
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Create type definitions in `src/@types/env.d.ts`:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
declare module '@env' {
|
|
64
|
+
export const MIXPANEL_TOKEN: string;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 3: Define Event Constants
|
|
69
|
+
|
|
70
|
+
Create `src/constants/tracking.ts`:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Event Names
|
|
74
|
+
export const Events = {
|
|
75
|
+
// Screen Views
|
|
76
|
+
SCREEN_VIEWED: 'Screen Viewed',
|
|
77
|
+
|
|
78
|
+
// User Actions
|
|
79
|
+
USER_SIGNED_UP: 'User Signed Up',
|
|
80
|
+
USER_LOGGED_IN: 'User Logged In',
|
|
81
|
+
USER_LOGGED_OUT: 'User Logged Out',
|
|
82
|
+
|
|
83
|
+
// Add your app-specific events here
|
|
84
|
+
} as const;
|
|
85
|
+
|
|
86
|
+
// Property Names
|
|
87
|
+
export const Properties = {
|
|
88
|
+
SCREEN_NAME: 'screen_name',
|
|
89
|
+
USER_ID: 'user_id',
|
|
90
|
+
TIMESTAMP: 'timestamp',
|
|
91
|
+
|
|
92
|
+
// Add your app-specific properties here
|
|
93
|
+
} as const;
|
|
94
|
+
|
|
95
|
+
// Super Property Keys
|
|
96
|
+
export const SuperProperties = {
|
|
97
|
+
APP_VERSION: 'App Version',
|
|
98
|
+
PLATFORM: 'Platform',
|
|
99
|
+
ENVIRONMENT: 'Environment',
|
|
100
|
+
} as const;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Why?** Constants prevent typos, enable autocomplete, and make refactoring easier.
|
|
104
|
+
|
|
105
|
+
### Step 4: Create Type Definitions
|
|
106
|
+
|
|
107
|
+
Create `src/types/mixpanel.types.ts`:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import {Mixpanel} from 'mixpanel-react-native';
|
|
111
|
+
|
|
112
|
+
export interface MixpanelContextValue {
|
|
113
|
+
mixpanel: Mixpanel | null;
|
|
114
|
+
isInitialized: boolean;
|
|
115
|
+
isLoading: boolean;
|
|
116
|
+
error: Error | null;
|
|
117
|
+
|
|
118
|
+
// Convenience methods
|
|
119
|
+
track: (eventName: string, properties?: Record<string, any>) => void;
|
|
120
|
+
identify: (distinctId: string) => void;
|
|
121
|
+
alias: (alias: string, distinctId?: string) => void;
|
|
122
|
+
reset: () => void;
|
|
123
|
+
flush: () => Promise<void>;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Step 5: Create Mixpanel Context
|
|
128
|
+
|
|
129
|
+
Create `src/contexts/MixpanelContext.tsx`:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import React, {
|
|
133
|
+
createContext,
|
|
134
|
+
useContext,
|
|
135
|
+
useEffect,
|
|
136
|
+
useState,
|
|
137
|
+
useCallback,
|
|
138
|
+
ReactNode,
|
|
139
|
+
} from 'react';
|
|
140
|
+
import {Mixpanel} from 'mixpanel-react-native';
|
|
141
|
+
import {Platform} from 'react-native';
|
|
142
|
+
import {MixpanelContextValue} from '../types/mixpanel.types';
|
|
143
|
+
import {SuperProperties} from '../constants/tracking';
|
|
144
|
+
|
|
145
|
+
const MixpanelContext = createContext<MixpanelContextValue | undefined>(
|
|
146
|
+
undefined,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
interface MixpanelProviderProps {
|
|
150
|
+
children: ReactNode;
|
|
151
|
+
token: string;
|
|
152
|
+
trackAutomaticEvents?: boolean;
|
|
153
|
+
useNative?: boolean;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const MixpanelProvider: React.FC<MixpanelProviderProps> = ({
|
|
157
|
+
children,
|
|
158
|
+
token,
|
|
159
|
+
trackAutomaticEvents = true,
|
|
160
|
+
useNative = true,
|
|
161
|
+
}) => {
|
|
162
|
+
const [mixpanel, setMixpanel] = useState<Mixpanel | null>(null);
|
|
163
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
164
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
165
|
+
const [error, setError] = useState<Error | null>(null);
|
|
166
|
+
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
const initMixpanel = async () => {
|
|
169
|
+
try {
|
|
170
|
+
setIsLoading(true);
|
|
171
|
+
setError(null);
|
|
172
|
+
|
|
173
|
+
const instance = new Mixpanel(token, trackAutomaticEvents, useNative);
|
|
174
|
+
await instance.init();
|
|
175
|
+
|
|
176
|
+
// Set default super properties
|
|
177
|
+
instance.registerSuperProperties({
|
|
178
|
+
[SuperProperties.APP_VERSION]: '1.0.0', // Replace with actual version
|
|
179
|
+
[SuperProperties.PLATFORM]: Platform.OS,
|
|
180
|
+
[SuperProperties.ENVIRONMENT]: __DEV__ ? 'development' : 'production',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (__DEV__) {
|
|
184
|
+
instance.setLoggingEnabled(true);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
setMixpanel(instance);
|
|
188
|
+
setIsInitialized(true);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
191
|
+
setError(error);
|
|
192
|
+
console.error('Failed to initialize Mixpanel:', error);
|
|
193
|
+
} finally {
|
|
194
|
+
setIsLoading(false);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
initMixpanel();
|
|
199
|
+
}, [token, trackAutomaticEvents, useNative]);
|
|
200
|
+
|
|
201
|
+
// Convenience wrapper methods
|
|
202
|
+
const track = useCallback(
|
|
203
|
+
(eventName: string, properties?: Record<string, any>) => {
|
|
204
|
+
if (!mixpanel || !isInitialized) {
|
|
205
|
+
console.warn('Mixpanel not initialized. Event:', eventName);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
mixpanel.track(eventName, properties);
|
|
209
|
+
},
|
|
210
|
+
[mixpanel, isInitialized],
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const identify = useCallback(
|
|
214
|
+
(distinctId: string) => {
|
|
215
|
+
if (!mixpanel || !isInitialized) return;
|
|
216
|
+
mixpanel.identify(distinctId);
|
|
217
|
+
},
|
|
218
|
+
[mixpanel, isInitialized],
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const alias = useCallback(
|
|
222
|
+
(aliasValue: string, distinctId?: string) => {
|
|
223
|
+
if (!mixpanel || !isInitialized) return;
|
|
224
|
+
mixpanel.alias(aliasValue, distinctId);
|
|
225
|
+
},
|
|
226
|
+
[mixpanel, isInitialized],
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const reset = useCallback(() => {
|
|
230
|
+
if (!mixpanel || !isInitialized) return;
|
|
231
|
+
mixpanel.reset();
|
|
232
|
+
}, [mixpanel, isInitialized]);
|
|
233
|
+
|
|
234
|
+
const flush = useCallback(async () => {
|
|
235
|
+
if (!mixpanel || !isInitialized) return;
|
|
236
|
+
await mixpanel.flush();
|
|
237
|
+
}, [mixpanel, isInitialized]);
|
|
238
|
+
|
|
239
|
+
const value: MixpanelContextValue = {
|
|
240
|
+
mixpanel,
|
|
241
|
+
isInitialized,
|
|
242
|
+
isLoading,
|
|
243
|
+
error,
|
|
244
|
+
track,
|
|
245
|
+
identify,
|
|
246
|
+
alias,
|
|
247
|
+
reset,
|
|
248
|
+
flush,
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<MixpanelContext.Provider value={value}>
|
|
253
|
+
{children}
|
|
254
|
+
</MixpanelContext.Provider>
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const useMixpanel = (): MixpanelContextValue => {
|
|
259
|
+
const context = useContext(MixpanelContext);
|
|
260
|
+
if (context === undefined) {
|
|
261
|
+
throw new Error('useMixpanel must be used within a MixpanelProvider');
|
|
262
|
+
}
|
|
263
|
+
return context;
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Step 6: Wrap Your App Root
|
|
268
|
+
|
|
269
|
+
In your root `App.tsx`:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import {MixpanelProvider} from './src/contexts/MixpanelContext';
|
|
273
|
+
import {MIXPANEL_TOKEN} from '@env';
|
|
274
|
+
|
|
275
|
+
function App() {
|
|
276
|
+
return (
|
|
277
|
+
<MixpanelProvider token={MIXPANEL_TOKEN} trackAutomaticEvents={true}>
|
|
278
|
+
{/* Your app content */}
|
|
279
|
+
</MixpanelProvider>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Step 7: Use in Components
|
|
285
|
+
|
|
286
|
+
Track events in any component:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import {useMixpanel} from '../contexts/MixpanelContext';
|
|
290
|
+
import {Events, Properties} from '../constants/tracking';
|
|
291
|
+
|
|
292
|
+
function MyScreen() {
|
|
293
|
+
const {track, isInitialized} = useMixpanel();
|
|
294
|
+
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
if (isInitialized) {
|
|
297
|
+
track(Events.SCREEN_VIEWED, {
|
|
298
|
+
[Properties.SCREEN_NAME]: 'MyScreen',
|
|
299
|
+
[Properties.TIMESTAMP]: new Date().toISOString(),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}, [isInitialized, track]);
|
|
303
|
+
|
|
304
|
+
const handleButtonClick = () => {
|
|
305
|
+
track('Button Clicked', {
|
|
306
|
+
button_name: 'Submit',
|
|
307
|
+
screen: 'MyScreen',
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
// Your UI
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Step 8: Test Your Integration
|
|
318
|
+
|
|
319
|
+
1. **Enable Logging** (in development):
|
|
320
|
+
```typescript
|
|
321
|
+
// Already enabled in context if __DEV__ is true
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
2. **Check Console**: Look for `[Mixpanel]` logs showing events being tracked
|
|
325
|
+
|
|
326
|
+
3. **Verify in Dashboard**:
|
|
327
|
+
- Go to Mixpanel dashboard
|
|
328
|
+
- Navigate to "Events" or "Live View"
|
|
329
|
+
- Confirm events are arriving
|
|
330
|
+
|
|
331
|
+
4. **Test Key Flows**:
|
|
332
|
+
```typescript
|
|
333
|
+
// Test user identification
|
|
334
|
+
identify('user123');
|
|
335
|
+
|
|
336
|
+
// Test event tracking
|
|
337
|
+
track('Test Event', {test: true});
|
|
338
|
+
|
|
339
|
+
// Test manual flush
|
|
340
|
+
await flush();
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Testing Validation Checklist
|
|
344
|
+
|
|
345
|
+
- [ ] Events appear in Mixpanel dashboard
|
|
346
|
+
- [ ] `getDistinctId()` returns a valid ID
|
|
347
|
+
- [ ] Console shows `[Mixpanel]` logs in development
|
|
348
|
+
- [ ] User profiles appear after `identify()` + `getPeople().set()`
|
|
349
|
+
- [ ] Super properties are included in all events
|
|
350
|
+
|
|
351
|
+
## Best Practices
|
|
352
|
+
|
|
353
|
+
### Event Naming Conventions
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
// ✅ Good: Noun + Verb (Past Tense)
|
|
357
|
+
'Product Viewed'
|
|
358
|
+
'Purchase Completed'
|
|
359
|
+
'Video Started'
|
|
360
|
+
|
|
361
|
+
// ❌ Bad: Vague or inconsistent
|
|
362
|
+
'click'
|
|
363
|
+
'ProductView'
|
|
364
|
+
'video_start'
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Property Naming Conventions
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// ✅ Good: snake_case, descriptive
|
|
371
|
+
{
|
|
372
|
+
product_id: 'prod-123',
|
|
373
|
+
product_name: 'Sample Product',
|
|
374
|
+
product_price: 29.99,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ❌ Bad: Inconsistent casing
|
|
378
|
+
{
|
|
379
|
+
ProductID: 'prod-123',
|
|
380
|
+
'product name': 'Sample Product',
|
|
381
|
+
price: 29.99,
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Code Organization
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
your-app/
|
|
389
|
+
├── src/
|
|
390
|
+
│ ├── contexts/
|
|
391
|
+
│ │ └── MixpanelContext.tsx # Keep in contexts/
|
|
392
|
+
│ ├── constants/
|
|
393
|
+
│ │ └── tracking.ts # Centralize event definitions
|
|
394
|
+
│ └── screens/
|
|
395
|
+
│ └── MyScreen.tsx # Import useMixpanel() as needed
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### When to Track
|
|
399
|
+
|
|
400
|
+
**✅ Track:**
|
|
401
|
+
- User actions (button clicks, form submissions)
|
|
402
|
+
- Navigation (screen views, tab switches)
|
|
403
|
+
- Key conversions (signup, purchase, subscription)
|
|
404
|
+
- Feature usage (search, filter, share)
|
|
405
|
+
|
|
406
|
+
**❌ Don't Track:**
|
|
407
|
+
- Every render or state change
|
|
408
|
+
- Internal errors (use error monitoring instead)
|
|
409
|
+
- PII without consent (email, phone without user permission)
|
|
410
|
+
|
|
411
|
+
## Common Pitfalls
|
|
412
|
+
|
|
413
|
+
### 1. Tracking Before Initialization
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// ❌ Bad
|
|
417
|
+
track('Event'); // Might not be initialized yet
|
|
418
|
+
|
|
419
|
+
// ✅ Good
|
|
420
|
+
if (isInitialized) {
|
|
421
|
+
track('Event');
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### 2. Forgetting to Alias
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// ❌ Bad: User's anonymous events are lost
|
|
429
|
+
identify(userId);
|
|
430
|
+
|
|
431
|
+
// ✅ Good: Preserve event history
|
|
432
|
+
const previousId = await mixpanel.getDistinctId();
|
|
433
|
+
identify(userId);
|
|
434
|
+
alias(userId, previousId);
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 3. Not Flushing Before Logout
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
// ❌ Bad: Queued events are lost
|
|
441
|
+
reset();
|
|
442
|
+
|
|
443
|
+
// ✅ Good: Send events before clearing
|
|
444
|
+
track('User Logged Out');
|
|
445
|
+
await flush();
|
|
446
|
+
reset();
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 4. Hardcoding Event Names
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
// ❌ Bad: Typos cause tracking issues
|
|
453
|
+
track('Screen Viewd'); // Typo!
|
|
454
|
+
|
|
455
|
+
// ✅ Good: Use constants
|
|
456
|
+
track(Events.SCREEN_VIEWED);
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 5. Over-Tracking
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
// ❌ Bad: Too noisy
|
|
463
|
+
onChange={(text) => track('Input Changed', {text})}
|
|
464
|
+
|
|
465
|
+
// ✅ Good: Track meaningful actions
|
|
466
|
+
onSubmit={() => track('Form Submitted', {form: 'login'})}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Essential Methods Reference
|
|
470
|
+
|
|
471
|
+
| Method | Purpose | Example |
|
|
472
|
+
|--------|---------|---------|
|
|
473
|
+
| `track()` | Send an event | `track('Button Clicked', {button: 'Submit'})` |
|
|
474
|
+
| `identify()` | Set user ID | `identify('user123')` |
|
|
475
|
+
| `alias()` | Link IDs | `alias('user123', 'anon-456')` |
|
|
476
|
+
| `getPeople().set()` | Set profile property | `getPeople().set({plan: 'premium'})` |
|
|
477
|
+
| `registerSuperProperties()` | Set global property | `registerSuperProperties({theme: 'dark'})` |
|
|
478
|
+
| `reset()` | Clear all data | `reset()` // Call on logout |
|
|
479
|
+
| `flush()` | Send queued events | `await flush()` // Before logout |
|
|
480
|
+
| `optInTracking()` | Enable tracking | `await optInTracking()` |
|
|
481
|
+
| `optOutTracking()` | Disable tracking | `await optOutTracking()` |
|
|
482
|
+
| `getDistinctId()` | Get current ID | `const id = await getDistinctId()` |
|
|
483
|
+
|
|
484
|
+
## Advanced Patterns
|
|
485
|
+
|
|
486
|
+
### User Identification Flow
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
const handleLogin = async (email: string, password: string) => {
|
|
490
|
+
// 1. Authenticate user (your auth logic)
|
|
491
|
+
const user = await authenticate(email, password);
|
|
492
|
+
|
|
493
|
+
// 2. Get previous anonymous ID
|
|
494
|
+
const previousId = await mixpanel.getDistinctId();
|
|
495
|
+
|
|
496
|
+
// 3. Identify with user ID
|
|
497
|
+
identify(user.id);
|
|
498
|
+
|
|
499
|
+
// 4. Link anonymous events to this user
|
|
500
|
+
alias(user.id, previousId);
|
|
501
|
+
|
|
502
|
+
// 5. Set user profile
|
|
503
|
+
mixpanel.getPeople().set({
|
|
504
|
+
$email: user.email,
|
|
505
|
+
$name: user.name,
|
|
506
|
+
plan: user.plan,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// 6. Track login event
|
|
510
|
+
track(Events.USER_LOGGED_IN, {
|
|
511
|
+
method: 'email',
|
|
512
|
+
timestamp: new Date().toISOString(),
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### GDPR-Compliant Logout
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
const handleLogout = async () => {
|
|
521
|
+
// 1. Track logout event
|
|
522
|
+
track(Events.USER_LOGGED_OUT, {
|
|
523
|
+
timestamp: new Date().toISOString(),
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// 2. Flush queued events
|
|
527
|
+
await flush();
|
|
528
|
+
|
|
529
|
+
// 3. Clear all Mixpanel data
|
|
530
|
+
reset();
|
|
531
|
+
|
|
532
|
+
// 4. Your app logout logic
|
|
533
|
+
await signOut();
|
|
534
|
+
};
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Timed Events
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
const handleVideoStart = () => {
|
|
541
|
+
// Start timer
|
|
542
|
+
mixpanel.timeEvent('Video Completed');
|
|
543
|
+
|
|
544
|
+
track('Video Started', {
|
|
545
|
+
video_id: 'intro-123',
|
|
546
|
+
video_title: 'Introduction',
|
|
547
|
+
});
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
const handleVideoEnd = () => {
|
|
551
|
+
// Duration is automatically calculated
|
|
552
|
+
track('Video Completed', {
|
|
553
|
+
video_id: 'intro-123',
|
|
554
|
+
video_title: 'Introduction',
|
|
555
|
+
});
|
|
556
|
+
};
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Troubleshooting
|
|
560
|
+
|
|
561
|
+
### Events Not Showing Up
|
|
562
|
+
|
|
563
|
+
1. Check token: `console.log(MIXPANEL_TOKEN)`
|
|
564
|
+
2. Verify initialization: `console.log(isInitialized)`
|
|
565
|
+
3. Check opt-out status: `const hasOptedOut = await mixpanel.hasOptedOutTracking()`
|
|
566
|
+
4. Enable logging: Already enabled if `__DEV__` is true
|
|
567
|
+
5. Manual flush: `await flush()`
|
|
568
|
+
|
|
569
|
+
### TypeScript Errors
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
# Clear Metro cache
|
|
573
|
+
npm start -- --reset-cache
|
|
574
|
+
|
|
575
|
+
# Reinstall pods (iOS)
|
|
576
|
+
cd ios && pod install && cd ..
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### Build Issues
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
# iOS
|
|
583
|
+
cd ios && xcodebuild clean && cd ..
|
|
584
|
+
|
|
585
|
+
# Android
|
|
586
|
+
cd android && ./gradlew clean && cd ..
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
## Next Steps
|
|
590
|
+
|
|
591
|
+
1. ✅ Complete the 8-step integration
|
|
592
|
+
2. ✅ Validate with the testing checklist
|
|
593
|
+
3. 📊 Define your tracking plan (which events, which properties)
|
|
594
|
+
4. 🎨 Customize super properties for your app
|
|
595
|
+
5. 🧪 Test in development before production
|
|
596
|
+
6. 📈 Monitor your Mixpanel dashboard
|
|
597
|
+
|
|
598
|
+
## Resources
|
|
599
|
+
|
|
600
|
+
- [Mixpanel React Native SDK Docs](https://docs.mixpanel.com/docs/tracking/advanced/react-native)
|
|
601
|
+
- [Mixpanel Best Practices](https://docs.mixpanel.com/docs/tracking/how-tos/events-and-properties)
|
|
602
|
+
- [MixpanelStarter Sample Code](./src)
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
**Questions?** Check the [README](./README.md) or [Mixpanel Community](https://community.mixpanel.com/).
|