UNPKG

mixpanel-react-native

Version:

Official React Native Tracking Library for Mixpanel Analytics

539 lines (435 loc) 15.8 kB
# CLAUDE.md - MixpanelStarter Sample App This file provides guidance to Claude Code when working with the MixpanelStarter sample application. ## Project Overview **Purpose**: Modern, production-ready React Native sample app demonstrating Mixpanel SDK integration with TypeScript, Context API, and best practices. **Target Audience**: Developers learning to integrate Mixpanel into their React Native apps. **Positioning**: Bridges the gap between SimpleMixpanel (too basic) and MixpanelDemo (too complex), demonstrating 80% of common use cases. ## Quick Reference ### Tech Stack - **React Native**: 0.82.1 - **React**: 19.1.1 - **TypeScript**: Strict mode enabled - **Navigation**: React Navigation 7.x with bottom tabs - **State Management**: Context API pattern - **Testing**: Jest with React Native Testing Library ### Key Dependencies - `mixpanel-react-native` - Main SDK (file:../..) - `@react-native-async-storage/async-storage` - Required for Mixpanel - `@react-navigation/native` + `@react-navigation/bottom-tabs` - Navigation - `react-native-dotenv` - Environment variables ## Commands ### Development ```bash # Install dependencies npm install cd ios && pod install && cd .. # Run app npm run ios # iOS simulator npm run android # Android emulator npm start # Start Metro bundler # Testing npm test # Run Jest tests npm test -- --coverage # With coverage npx tsc --noEmit # TypeScript compilation check # Linting npm run lint # Run ESLint ``` ### Environment Setup ```bash # Create environment file cp .env.example .env # Edit .env and set: MIXPANEL_TOKEN=your_token_here ``` ### Troubleshooting Commands ```bash # Clear Metro cache npm start -- --reset-cache # Clean iOS build cd ios && xcodebuild clean && cd .. # Clean Android build cd android && ./gradlew clean && cd .. # Reinstall dependencies rm -rf node_modules && npm install ``` ## Architecture & Code Patterns ### Core Architecture Pattern: Context API + Custom Hook ```typescript // Provider wraps app root (src/App.tsx) <MixpanelProvider token={token} trackAutomaticEvents={true}> <NavigationContainer> {/* app content */} </NavigationContainer> </MixpanelProvider> // Hook usage in any component const { mixpanel, isInitialized, track, identify } = useMixpanel(); ``` **Why Context API?** - Simple, no boilerplate - Built into React (widely understood) - Sufficient for Mixpanel's simple state needs - Avoids prop drilling ### File Naming Conventions - **Components**: PascalCase (ActionButton.tsx, InfoCard.tsx) - **Screens**: PascalCase with "Screen" suffix (OnboardingScreen.tsx) - **Contexts**: PascalCase with "Context" suffix (MixpanelContext.tsx) - **Types**: kebab-case with .types.ts suffix (mixpanel.types.ts) - **Constants**: kebab-case (tracking.ts) ### Import Order Pattern ```typescript // 1. External libraries (React, React Native) import React, {useState, useEffect} from 'react'; import {View, Text, StyleSheet} from 'react-native'; // 2. Navigation import {NavigationContainer} from '@react-navigation/native'; // 3. Internal contexts/hooks import {useMixpanel} from '../contexts/MixpanelContext'; // 4. Components import {ActionButton} from '../components/ActionButton'; // 5. Constants/types import {Events, Properties} from '../constants/tracking'; ``` ### Event Tracking Pattern ```typescript // Always use constants (prevents typos, enables autocomplete) import {Events, Properties} from '../constants/tracking'; // Check initialization before tracking if (isInitialized) { track(Events.SCREEN_VIEWED, { [Properties.SCREEN_NAME]: 'Home', [Properties.TIMESTAMP]: new Date().toISOString(), }); } // Track on mount with useEffect useEffect(() => { if (isInitialized) { track(Events.SCREEN_VIEWED, {...}); } }, [isInitialized, track]); ``` ### User Identification Pattern ```typescript // Complete signup flow (OnboardingScreen.tsx:52-90) const previousId = await mixpanel.getDistinctId(); identify(userId); // Set new identity await alias(userId, previousId); // Link anonymous events mixpanel.getPeople().set({...}); // Set profile mixpanel.getPeople().setOnce({...}); // Set immutable properties track(Events.USER_SIGNED_UP, {...}); // Track event ``` ### Privacy-Compliant Logout Pattern ```typescript // SettingsScreen.tsx demonstrates GDPR-compliant logout track('User Logged Out', {...}); // Track before clearing await flush(); // Ensure events sent reset(); // Clear all data ``` ## Project Structure ``` MixpanelStarter/ ├── src/ │ ├── contexts/ │ │ └── MixpanelContext.tsx # Context provider + useMixpanel hook │ ├── screens/ # 3 tab screens │ │ ├── OnboardingScreen.tsx # User identification flow │ │ ├── HomeScreen.tsx # Event tracking patterns │ │ └── SettingsScreen.tsx # Privacy controls │ ├── components/ # Reusable components │ │ ├── ErrorBoundary.tsx # Error boundary wrapper │ │ ├── ActionButton.tsx # Button with loading states │ │ └── InfoCard.tsx # Key-value display card │ ├── types/ │ │ └── mixpanel.types.ts # TypeScript interfaces │ ├── constants/ │ │ └── tracking.ts # Event names & properties │ ├── @types/ │ │ └── env.d.ts # Environment variable types │ └── App.tsx # Navigation setup ├── __tests__/ │ └── MixpanelContext.test.tsx # Context tests ├── ios/ # iOS native code ├── android/ # Android native code ├── .env.example # Environment template ├── README.md # User-facing documentation └── INTEGRATION_GUIDE.md # Step-by-step integration guide ``` ### File Responsibilities **MixpanelContext.tsx** (src/contexts/) - Initializes Mixpanel SDK on mount - Sets default super properties (app version, platform, environment) - Provides convenience wrapper methods (track, identify, alias, reset, flush) - Manages loading/error states - Exports useMixpanel hook **OnboardingScreen.tsx** (src/screens/) - Demonstrates: identify(), alias(), getPeople().set(), setOnce() - Shows anonymous-to-identified user flow - Displays current distinct ID - Guest mode vs signup comparison **HomeScreen.tsx** (src/screens/) - Demonstrates: track(), timeEvent(), registerSuperProperties() - Event tracking with rich properties - Timed events (video start/complete) - Dynamic super properties (dark mode, notifications) - Displays current super properties **SettingsScreen.tsx** (src/screens/) - Demonstrates: optIn/OutTracking(), reset(), flush(), hasOptedOutTracking() - GDPR compliance controls - Data management (reset all data) - Manual flush for testing - SDK information display **tracking.ts** (src/constants/) - Centralized event names (Events object) - Property names (Properties object) - Super property keys (SuperProperties object) - All constants are `as const` for type safety ## Key Concepts Demonstrated ### 1. Initialization Lifecycle ```typescript // Context handles initialization automatically useEffect(() => { const initMixpanel = async () => { const instance = new Mixpanel(token, trackAutomaticEvents, useNative); await instance.init(); instance.registerSuperProperties({...}); setMixpanel(instance); setIsInitialized(true); }; initMixpanel(); }, [token]); ``` ### 2. Loading State Management ```typescript // Components can check initialization state const { isInitialized, isLoading, error } = useMixpanel(); // Disable buttons until ready <ActionButton disabled={!isInitialized} onPress={handleAction} /> ``` ### 3. Error Handling ```typescript // Context catches initialization errors try { await instance.init(); } catch (err) { setError(err instanceof Error ? err : new Error(String(err))); console.error('Failed to initialize Mixpanel:', error); } // ErrorBoundary catches React errors <ErrorBoundary> <MixpanelProvider> {/* app */} </MixpanelProvider> </ErrorBoundary> ``` ### 4. TypeScript Strict Mode - All files compile without errors - Proper typing for all props and state - Event/property names are type-safe constants - No `any` types except in Record<string, any> for event properties ### 5. Educational Components Every screen includes an InfoCard titled "What's Happening?" that explains: - Which SDK methods are being used - Why you'd use this pattern - What data is being sent ## Common Modifications ### Adding a New Event 1. **Define constant** (src/constants/tracking.ts): ```typescript export const Events = { // ... existing events FEATURE_USED: 'Feature Used', }; ``` 2. **Track in component**: ```typescript const handleFeatureUse = () => { track(Events.FEATURE_USED, { feature_name: 'search', timestamp: new Date().toISOString(), }); }; ``` ### Adding a New Screen 1. **Create screen file** (src/screens/NewScreen.tsx): ```typescript import {useMixpanel} from '../contexts/MixpanelContext'; import {Events, Properties} from '../constants/tracking'; export const NewScreen = () => { const {track, isInitialized} = useMixpanel(); useEffect(() => { if (isInitialized) { track(Events.SCREEN_VIEWED, { [Properties.SCREEN_NAME]: 'NewScreen', }); } }, [isInitialized, track]); // ... component logic }; ``` 2. **Add to navigation** (src/App.tsx): ```typescript <Tab.Screen name="New" component={NewScreen} options={{ tabBarLabel: 'New', tabBarIcon: ({color}) => <Text style={{fontSize: 20, color}}>🎯</Text>, }} /> ``` ### Changing Mixpanel Configuration **In MixpanelProvider** (src/contexts/MixpanelContext.tsx): ```typescript // Change automatic event tracking trackAutomaticEvents={false} // Change native/JS mode useNative={false} // Forces JavaScript implementation // Modify default super properties instance.registerSuperProperties({ [SuperProperties.APP_VERSION]: '2.0.0', // Update version 'Custom Property': 'value', // Add custom property }); ``` ## Testing Guidelines ### Running Tests ```bash npm test # Run all tests npm test -- --coverage # With coverage report npm test -- --watch # Watch mode ``` ### Test Structure - **Unit tests**: Components and contexts in isolation - **Mock Mixpanel SDK**: Already configured in MixpanelContext.test.tsx - **Test initialization**: Verify loading → initialized state transition - **Test error handling**: Provider catches and exposes errors ### Writing New Tests ```typescript // Mock Mixpanel SDK jest.mock('mixpanel-react-native', () => ({ Mixpanel: jest.fn().mockImplementation(() => ({ init: jest.fn().mockResolvedValue(undefined), track: jest.fn(), // ... other methods })), })); // Test component with context render( <MixpanelProvider token="test-token"> <YourComponent /> </MixpanelProvider> ); ``` ## TypeScript Configuration ### Strict Mode Enabled ```json { "compilerOptions": { "strict": true, // All strict checks enabled "typeRoots": ["./node_modules/@types", "./src/@types"] } } ``` ### Type Checking ```bash npx tsc --noEmit # Check types without emitting files ``` ### Common Type Issues - **Environment variables**: Defined in src/@types/env.d.ts - **Mixpanel instance**: Can be null before initialization (check isInitialized) - **Async methods**: alias() and flush() return Promise<void> ## Debugging ### Enable Logging Logging is automatically enabled in development (`__DEV__`): ```typescript if (__DEV__) { instance.setLoggingEnabled(true); } ``` ### Console Output Look for `[Mixpanel]` prefixed logs: - Event tracking confirmations - Network requests - Error messages - Queue status ### Verify Events 1. **Console**: Check for `[Mixpanel]` logs 2. **Network**: Use React Native Debugger to see network requests 3. **Dashboard**: Events appear in Mixpanel Live View (may take 1-2 minutes) 4. **Distinct ID**: Call `await mixpanel.getDistinctId()` to verify initialization ### Common Issues **Events not tracked?** - Check `isInitialized` is true - Verify token is set correctly - Check opt-out status: `await hasOptedOutTracking()` - Manually flush: `await flush()` **TypeScript errors?** - Clear Metro cache: `npm start -- --reset-cache` - Verify all dependencies installed: `npm install` - Check tsconfig.json is correct **Build errors?** - iOS: `cd ios && pod install && cd ..` - Android: `cd android && ./gradlew clean && cd ..` - Clear node_modules: `rm -rf node_modules && npm install` ## Best Practices for Modifications ### DO: ✅ Use constants for event/property names ✅ Check `isInitialized` before tracking ✅ Include timestamps in event properties ✅ Add "What's Happening?" explanations for new features ✅ Follow existing file structure and naming conventions ✅ Update TypeScript types when adding new interfaces ✅ Test on both iOS and Android ✅ Keep Context API pattern for Mixpanel access ### DON'T: ❌ Hardcode event names (use constants) ❌ Track before initialization ❌ Use `any` type (use strict TypeScript) ❌ Skip error handling ❌ Track PII without user consent ❌ Over-track (every render/state change) ❌ Mix navigation patterns (stick to bottom tabs) ## Educational Purpose This sample app is designed to teach, not just demonstrate. When making changes: 1. **Preserve educational value**: Keep "What's Happening?" cards updated 2. **Maintain simplicity**: This bridges simple→complex, don't overcomplicate 3. **Document patterns**: Add comments explaining WHY, not just WHAT 4. **Keep it copy-pasteable**: Developers should be able to copy patterns directly ## Integration with Parent SDK This sample lives at: `Samples/MixpanelStarter/` **SDK dependency**: Uses `file:../..` to reference parent mixpanel-react-native package **When SDK changes**: ```bash cd Samples/MixpanelStarter rm -rf node_modules npm install # Re-links to parent SDK ``` ## Documentation Maintenance ### When to Update Docs **README.md**: User-facing changes (new features, setup steps, troubleshooting) **INTEGRATION_GUIDE.md**: Integration patterns, API changes, best practices **CLAUDE.md**: Architecture changes, new patterns, file structure updates ### Documentation Standards - Keep examples up-to-date with actual code - Include both "why" and "how" explanations - Use code snippets from real files (with file paths) - Maintain consistent formatting and style ## Version Information **Created**: 2025 **React Native Version**: 0.82.1 **Mixpanel SDK**: 3.1.2 **Target RN Version**: 0.70+ ## AI Assistant Notes When working with this codebase: 1. **Preserve the educational mission**: This is a learning tool, not just a demo 2. **Maintain Context API pattern**: Don't switch to Redux/MobX/etc 3. **Keep TypeScript strict**: No `any` types, proper inference 4. **Follow existing patterns**: File naming, import order, component structure 5. **Test changes**: Run `npm test` and `npx tsc --noEmit` before completing 6. **Update documentation**: README, INTEGRATION_GUIDE, and CLAUDE.md as needed ## Resources - **Parent SDK**: `../../` (mixpanel-react-native) - **Parent CLAUDE.md**: `../../CLAUDE.md` - **Mixpanel Docs**: https://docs.mixpanel.com/docs/tracking/advanced/react-native - **React Navigation**: https://reactnavigation.org/ Last updated: 2025-05-11