Skip to content

feat: create new useAnalytics hook to replace useMetrics #25036

@NicolasMassart

Description

@NicolasMassart

What is this about?

This issue tracks the migration from the existing useMetrics hook and withMetricsAwareness HOC to a new useAnalytics hook and withAnalyticsAwareness HOC that directly uses the analytics utility from app/util/analytics/analytics instead of going through the MetaMetrics layer.

Objectives

  1. Create new useAnalytics hook - Build a new hook in app/components/hooks/useAnalytics that:

    • Uses analytics from app/util/analytics/analytics as the underlying implementation
    • Maintains the same API interface as useMetrics for backward compatibility
    • Provides all the same methods: trackEvent, createEventBuilder, enable, addTraitsToUser, isEnabled, getMetaMetricsId, and data deletion methods
  2. Create withAnalyticsAwareness HOC - Build a new Higher-Order Component in app/components/hooks/useAnalytics that:

    • Follows the same pattern as withMetricsAwareness
    • Injects the useAnalytics hook into component props
    • Maintains the same prop interface for compatibility
  3. Deprecate existing implementations - Mark useMetrics and withMetricsAwareness as deprecated:

    • Add @deprecated JSDoc tags to both exports
    • Add deprecation notices in code comments
    • Update documentation to indicate the migration path

Scenario

No response

Design

No response

Technical Details

Implementation Details

  • Hook Location: app/components/hooks/useAnalytics/useAnalytics.ts
  • HOC Location: app/components/hooks/useAnalytics/withAnalyticsAwareness.tsx
  • Underlying Implementation: Uses analytics utility from app/util/analytics/analytics instead of MetaMetrics singleton
  • Event Builder: Uses AnalyticsEventBuilder from app/util/analytics/AnalyticsEventBuilder for event construction
  • API Compatibility: Maintains exact same interface as useMetrics hook (IUseMetricsHook)

Key Implementation Points

  1. Hook Implementation:

    • Use useMemo to memoize the hook return value (same pattern as useMetrics)
    • Delegate trackEvent to analytics.trackEvent with proper event conversion
    • Delegate enable to analytics.optIn/analytics.optOut
    • Delegate addTraitsToUser to analytics.identify
    • Delegate isEnabled to analytics.isEnabled
    • Delegate getMetaMetricsId to analytics.getAnalyticsId
    • For data deletion methods, continue delegating to MetaMetrics.getInstance() until fully migrated
  2. HOC Implementation:

    • Follow same pattern as withMetricsAwareness
    • Use TypeScript generics to ensure prop type safety
    • Inject useAnalytics() result as analytics prop
  3. Deprecation:

    • Add @deprecated JSDoc tags to useMetrics and withMetricsAwareness
    • Add deprecation notices in code comments
    • Update exports to indicate migration path

Global Test Mock

Create a global mock for useAnalytics in app/util/test/testSetup.js so developers don't need to mock it in each test file. The mock should:

  • Provide all methods from IUseMetricsHook interface with sensible defaults:

    • trackEvent: jest.fn()
    • createEventBuilder: jest.fn() returning a builder with addProperties, addSensitiveProperties, build methods
    • isEnabled: jest.fn().mockReturnValue(true)
    • enable: jest.fn().mockResolvedValue(undefined)
    • addTraitsToUser: jest.fn().mockResolvedValue(undefined)
    • createDataDeletionTask: jest.fn().mockResolvedValue({ regulationId: 'mock-id' })
    • checkDataDeleteStatus: jest.fn().mockResolvedValue({ status: 'pending' })
    • getDeleteRegulationCreationDate: jest.fn().mockReturnValue(new Date())
    • getDeleteRegulationId: jest.fn().mockReturnValue('mock-regulation-id')
    • isDataRecorded: jest.fn().mockReturnValue(true)
    • getMetaMetricsId: jest.fn().mockResolvedValue('mock-analytics-id')
  • Mock both the hook and HOC exports:

      jest.mock('../../components/hooks/useAnalytics', () => ({
        useAnalytics: jest.fn(() => ({ /* mock implementation */ })),
        withAnalyticsAwareness: jest.fn((Component) => Component),
      }));
    

Allow individual tests to override specific methods using jest.mocked(useAnalytics).mockReturnValue() when needed

Files to Create:

  • app/components/hooks/useAnalytics/useAnalytics.ts
  • app/components/hooks/useAnalytics/useAnalytics.types.ts
  • app/components/hooks/useAnalytics/withAnalyticsAwareness.tsx
  • app/components/hooks/useAnalytics/withAnalyticsAwareness.types.ts
  • app/components/hooks/useAnalytics/index.ts
  • app/components/hooks/useAnalytics/useAnalytics.test.tsx
  • app/components/hooks/useAnalytics/withAnalyticsAwareness.test.tsx

Files to Update:

  • app/components/hooks/useMetrics/useMetrics.ts - Add @deprecated tag
  • app/components/hooks/useMetrics/withMetricsAwareness.tsx - Add @deprecated tag
  • app/components/hooks/useMetrics/index.ts - Add deprecation notices
  • app/util/test/testSetup.js - Add global mock for useAnalytics

Threat Modeling Framework

  • What are we working on? Creating a new useAnalytics hook and withAnalyticsAwareness HOC that directly use the analytics utility instead of the MetaMetrics singleton, reducing legacy coupling.

  • What can go wrong? Hook not implementing same features as the original useMetrics.

  • What are we going to do about it? Ensure full TypeScript coverage with strict types, maintain exact API parity with useMetrics, use useMemo for proper memoization, add runtime validation for hook usage, use TypeScript generics for HOC prop safety, delegate all methods correctly to analytics utility, provide a proper global mock for the hook so devs don't have to implement it in their tests each time, and achieve comprehensive test coverage comparing behavior with useMetrics.

  • Did we do a good job? Success criteria: 100% API compatibility verified through side-by-side testing, full TypeScript coverage with no any types, >90% test coverage, no runtime errors, proper memoization preventing unnecessary re-renders, global mock available for easy test setup, and clear deprecation notices; validated through TypeScript compilation, automated test suite, and migration testing.

Acceptance Criteria

  • New useAnalytics hook created with full test coverage
  • New withAnalyticsAwareness HOC created with full test coverage
  • All existing functionality preserved (trackEvent, createEventBuilder, enable, addTraitsToUser, isEnabled, getMetaMetricsId, data deletion methods)
  • useMetrics and withMetricsAwareness marked as deprecated with appropriate JSDoc tags

Stakeholder review needed before the work gets merged

  • Engineering (needed in most cases)
  • Design
  • Product
  • QA (automation tests are required to pass before merging PRs but not all changes are covered by automation tests - please review if QA is needed beyond automation tests)
  • Security
  • Legal
  • Marketing
  • Management (please specify)
  • Other (please specify)

References

  • app/core/Analytics/README.md - Analytics module documentation
  • app/components/hooks/useMetrics/useMetrics.ts - Current implementation reference
  • app/components/hooks/useMetrics/withMetricsAwareness.tsx - Current HOC pattern
  • app/util/analytics/analytics.ts - Target analytics utility
  • app/util/analytics/AnalyticsEventBuilder.ts - Event builder utility
  • app/components/hooks/useMetrics/useMetrics.types.ts - Type definitions
  • app/util/test/testSetup.js - Global test mocks location
  • .cursor/rules/unit-testing-guidelines.mdc - Unit testing standards
  • .cursor/rules/component-view-testing.mdc - Component testing patterns

Metadata

Metadata

Labels

release-7.64.0Issue or pull request that will be included in release 7.64.0release-7.65.0Issue or pull request that will be included in release 7.65.0
No fields configured for Sub-Task.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions