Skip to content

feat(react): implement useInteractionTrace hook with pre-typed API#14

Merged
frankieyan merged 5 commits into
mainfrom
frankieyan/core-implementation-v4
Feb 5, 2026
Merged

feat(react): implement useInteractionTrace hook with pre-typed API#14
frankieyan merged 5 commits into
mainfrom
frankieyan/core-implementation-v4

Conversation

@frankieyan

@frankieyan frankieyan commented Feb 3, 2026

Copy link
Copy Markdown
Member

This PR adds a React hook useInteractionTrace that provides a declarative way to sign interaction traces when components mount. It follows the same withTypes() pattern as signInteractionTrace for compile-time type safety.

Stacked on top of #11
Documentation updates: #17

Example

import { useInteractionTrace } from '@doist/interaction-trace/react'

// Basic usage - signs trace on mount
function SettingsModal() {
    useInteractionTrace('open modal', { modalId: 'settings' })
    return <div>...</div>
}

// With pre-typed API (recommended)
type MyTraces = {
    'open modal': { modalId: string }
    'submit form': { formId: string; success: boolean }
}

const useTrace = useInteractionTrace.withTypes<MyTraces>()

function SettingsModal() {
    useTrace('open modal', { modalId: 'settings' })  // ✅ autocomplete works!
    return <div>...</div>
}

// Errors caught at compile time:
useTrace('opne modal', { modalId: 'settings' })  // ❌ Error: typo caught
useTrace('open modal', { formId: 'wrong' })      // ❌ Error: wrong details shape

Test Plan

To verify the type safety works:

  1. Create a pre-typed hook:

    type MyTraces = {
        'test trace': { id: string }
    }
    const useTrace = useInteractionTrace.withTypes<MyTraces>()
  2. Verify TypeScript catches errors:

    • Invalid trace names (e.g., useTrace('typo trace', ...))
    • Wrong payload shapes (e.g., useTrace('test trace', { wrong: 123 }))
    • Missing required properties (e.g., useTrace('test trace', {}))
  3. Verify autocomplete suggests 'test trace' when typing the first argument

@frankieyan frankieyan changed the base branch from main to frankieyan/core-implementation-v3 February 3, 2026 08:19
@frankieyan frankieyan force-pushed the frankieyan/core-implementation-v3 branch from c43d3ef to 4dc5271 Compare February 4, 2026 02:09
Base automatically changed from frankieyan/core-implementation-v3 to main February 4, 2026 02:10
@frankieyan frankieyan force-pushed the frankieyan/core-implementation-v4 branch from fb99e1b to f640248 Compare February 4, 2026 02:19
@frankieyan frankieyan marked this pull request as ready for review February 4, 2026 03:07
@frankieyan frankieyan requested a review from a team as a code owner February 4, 2026 03:07
@frankieyan frankieyan requested review from Bloomca and removed request for a team February 4, 2026 03:07

@doistbot-app doistbot-app Bot 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.

This PR introduces a useInteractionTrace React hook, offering a declarative and type-safe way to sign interaction traces on component mount, complete with a withTypes() pattern for enhanced compile-time safety. This feature improves the developer experience by providing robust interaction tracing, and no issues were flagged during the review.

frankieyan and others added 4 commits February 3, 2026 22:59
Add React 19.2.4 to devDependencies for testing React hooks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add useInteractionTrace hook that signs traces on component mount
- Support .withTypes<TDefs>() for compile-time type safety
- Include SSR guard (no-op when window is undefined)
- Extract shared TraceDefinitions types to src/trace-definitions.mts
- Add comprehensive tests including type inference validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…race tests

- Replace inline PerformanceObserver mock with createPerformanceObserverMock
  and createMockObserverRegistry from test-utils/performance-mocks
- Add createPerformanceMock to properly handle performance.mark/measure
  when using vi.useFakeTimers() (HappyDOM breaks when performance.now is faked)
- Add vi.runOnlyPendingTimers() before vi.useRealTimers() in all test suites
  per testing-library best practices to flush pending timers before cleanup
- Simplify test assertions using observerRegistry.triggerCallback()

This makes the React hook tests consistent with the core test files and
reduces code duplication by ~40 lines.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@frankieyan frankieyan force-pushed the frankieyan/core-implementation-v4 branch from 66b57af to c8a8e89 Compare February 4, 2026 07:06
The untyped API is a first-class option for users who don't need type
enforcement, not a deprecated pattern.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@Bloomca Bloomca 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.

I am not sure how to test it, but the code looks good 👍

@frankieyan

Copy link
Copy Markdown
Member Author

@Bloomca just something like this to verify type checking works should be enough:

image

@frankieyan frankieyan merged commit dca346d into main Feb 5, 2026
2 checks passed
@frankieyan frankieyan deleted the frankieyan/core-implementation-v4 branch February 5, 2026 05:58
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.

2 participants