メインコンテンツへスキップ
8分で読めます

React Integration Example

This guide provides a complete, production-ready example of integrating Zenovay analytics into a React application using modern React patterns and hooks.

Quick Start

Add the Zenovay tracking script to your index.html:

<!-- public/index.html (Create React App) or index.html (Vite) -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script defer data-tracking-code="YOUR_TRACKING_CODE" src="https://api.zenovay.com/z.js"></script>
  </body>
</html>

That's it for basic page view tracking. The script automatically tracks page views on load.

Basic Setup

1. Create a Zenovay Hook

Create a reusable hook for tracking throughout your React app:

// hooks/useZenovay.ts
export function useZenovay() {
  const track = (eventName: string, eventData: Record<string, any> = {}) => {
    if (window.zenovay) {
      window.zenovay('track', eventName, eventData);
    }
  };

  const identify = (userId: string, traits: Record<string, any> = {}) => {
    if (window.zenovay) {
      window.zenovay('identify', userId, traits);
    }
  };

  const trackGoal = (goalName: string, data: Record<string, any> = {}) => {
    if (window.zenovay) {
      window.zenovay('goal', goalName, data);
    }
  };

  const trackPurchase = (data: { amount: number; currency: string; product: string }) => {
    if (window.zenovay) {
      window.zenovay('revenue', data.amount, data.currency);
    }
  };

  return { track, identify, trackGoal, trackPurchase };
}

2. Track Page Views (React Router)

For single-page applications using React Router:

// App.tsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useZenovay } from './hooks/useZenovay';

function PageTracker() {
  const location = useLocation();
  const { track } = useZenovay();

  useEffect(() => {
    track('pageview', {
      path: location.pathname + location.search,
      title: document.title,
    });
  }, [location.pathname]);

  return null;
}

function App() {
  return (
    <Routes>
      {/* Add PageTracker inside your router */}
      <PageTracker />
      {/* your routes */}
    </Routes>
  );
}

3. Track Custom Events

import { useZenovay } from './hooks/useZenovay';

function SignupButton() {
  const { track } = useZenovay();

  const handleSignup = () => {
    track('signup_started', {
      plan: 'professional',
      source: 'pricing_page'
    });

    // Continue with signup logic
  };

  return <button onClick={handleSignup}>Start Free Trial</button>;
}

Complete Example

Here's a full implementation with all common use cases:

Page View Tracking Hook

// hooks/usePageTracking.ts
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export function usePageTracking() {
  const location = useLocation();

  useEffect(() => {
    // Track pageview on route change
    if (window.zenovay) {
      window.zenovay('track', 'pageview', {
        path: location.pathname + location.search,
        referrer: document.referrer,
        title: document.title,
      });
    }
  }, [location.pathname, location.search]);
}

Event Tracking Component

// components/TrackableButton.tsx
import { useZenovay } from '../hooks/useZenovay';
import { ButtonHTMLAttributes } from 'react';

interface TrackableButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  eventName: string;
  eventData?: Record<string, any>;
  children: React.ReactNode;
}

export function TrackableButton({
  eventName,
  eventData,
  onClick,
  children,
  ...props
}: TrackableButtonProps) {
  const { track } = useZenovay();

  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    // Track the event
    track(eventName, eventData);

    // Call original onClick if provided
    onClick?.(e);
  };

  return (
    <button onClick={handleClick} {...props}>
      {children}
    </button>
  );
}

Usage Example

// pages/PricingPage.tsx
import { TrackableButton } from '../components/TrackableButton';

export function PricingPage() {
  return (
    <div>
      <h1>Pricing</h1>

      <TrackableButton
        eventName="plan_selected"
        eventData={{
          plan: 'scale',
          billing: 'monthly',
          price: 90
        }}
        className="btn-primary"
      >
        Choose Scale
      </TrackableButton>
    </div>
  );
}

Advanced Patterns

Conditional Tracking

Track events only when certain conditions are met:

import { useZenovay } from '../hooks/useZenovay';
import { useEffect } from 'react';

function ProductPage({ product }) {
  const { track } = useZenovay();

  useEffect(() => {
    // Only track if product is high-value
    if (product.price > 1000) {
      track('high_value_product_viewed', {
        product_id: product.id,
        product_name: product.name,
        price: product.price
      });
    }
  }, [product]);

  return <div>{/* product details */}</div>;
}

Form Tracking

Track form interactions and submissions:

import { useZenovay } from '../hooks/useZenovay';
import { useState, FormEvent } from 'react';

function ContactForm() {
  const { track } = useZenovay();
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();

    // Track form submission
    track('contact_form_submitted', {
      form_id: 'contact',
      has_message: formData.message.length > 0
    });

    // Submit form logic
  };

  const handleFocus = (field: string) => {
    // Track field focus
    track('form_field_focused', {
      form_id: 'contact',
      field_name: field
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={formData.name}
        onChange={(e) => setFormData({ ...formData, name: e.target.value })}
        onFocus={() => handleFocus('name')}
        placeholder="Name"
      />
      <input
        type="email"
        value={formData.email}
        onChange={(e) => setFormData({ ...formData, email: e.target.value })}
        onFocus={() => handleFocus('email')}
        placeholder="Email"
      />
      <textarea
        value={formData.message}
        onChange={(e) => setFormData({ ...formData, message: e.target.value })}
        onFocus={() => handleFocus('message')}
        placeholder="Message"
      />
      <button type="submit">Send Message</button>
    </form>
  );
}

E-commerce Tracking

Track product views, add to cart, and purchases:

import { useZenovay } from '../hooks/useZenovay';

function useEcommerceTracking() {
  const { track, trackPurchase } = useZenovay();

  const trackProductView = (product) => {
    track('product_viewed', {
      product_id: product.id,
      product_name: product.name,
      category: product.category,
      price: product.price,
      currency: 'USD'
    });
  };

  const trackAddToCart = (product, quantity = 1) => {
    track('product_added_to_cart', {
      product_id: product.id,
      product_name: product.name,
      quantity,
      price: product.price,
      total: product.price * quantity
    });
  };

  const trackCheckout = (cart) => {
    track('checkout_started', {
      cart_total: cart.total,
      item_count: cart.items.length,
      currency: 'USD'
    });
  };

  const trackPurchaseCompleted = (order) => {
    trackPurchase({
      amount: order.total,
      currency: 'USD',
      product: `Order #${order.id}`,
    });

    track('purchase_completed', {
      order_id: order.id,
      revenue: order.total,
      tax: order.tax,
      shipping: order.shipping,
      currency: 'USD',
      items: order.items.map(item => ({
        product_id: item.id,
        quantity: item.quantity,
        price: item.price
      }))
    });
  };

  return {
    trackProductView,
    trackAddToCart,
    trackCheckout,
    trackPurchaseCompleted
  };
}

// Usage
function ProductPage({ product }) {
  const { trackProductView, trackAddToCart } = useEcommerceTracking();

  useEffect(() => {
    trackProductView(product);
  }, [product]);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>${product.price}</p>
      <button onClick={() => trackAddToCart(product)}>
        Add to Cart
      </button>
    </div>
  );
}

Error Boundary Tracking

Track errors automatically:

import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
}

export class ErrorBoundary extends Component<Props, State> {
  public state: State = {
    hasError: false
  };

  public static getDerivedStateFromError(): State {
    return { hasError: true };
  }

  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // Track error via global zenovay object
    if (window.zenovay) {
      window.zenovay('track', 'error_occurred', {
        error_message: error.message,
        error_stack: error.stack,
        component_stack: errorInfo.componentStack,
        error_boundary: 'AppErrorBoundary'
      });
    }

    console.error('Uncaught error:', error, errorInfo);
  }

  public render() {
    if (this.state.hasError) {
      return this.props.fallback || <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

User Identification

Track identified users:

import { useZenovay } from '../hooks/useZenovay';
import { useEffect } from 'react';
import { useAuth } from './auth-context';

function UserIdentifier() {
  const { identify } = useZenovay();
  const { user } = useAuth();

  useEffect(() => {
    if (user) {
      identify(user.id, {
        email: user.email,
        name: user.name,
        plan: user.subscription?.plan,
        signup_date: user.createdAt
      });
    }
  }, [user]);

  return null;
}

// Add to your app layout
function App() {
  return (
    <>
      <UserIdentifier />
      {/* rest of app */}
    </>
  );
}

TypeScript Support

Full TypeScript support with typed events:

// types/analytics.ts
export interface AnalyticsEvents {
  signup_started: {
    plan: 'free' | 'pro' | 'scale' | 'enterprise';
    source: string;
  };
  product_viewed: {
    product_id: string;
    product_name: string;
    price: number;
  };
  purchase_completed: {
    order_id: string;
    revenue: number;
    currency: string;
  };
}

// Declare the global zenovay object
declare global {
  interface Window {
    zenovay: (...args: any[]) => void;
  }
}

// hooks/useZenovay.ts - Typed version
import type { AnalyticsEvents } from '../types/analytics';

export function useZenovay() {
  const track = <K extends keyof AnalyticsEvents>(
    eventName: K,
    data: AnalyticsEvents[K]
  ) => {
    if (window.zenovay) {
      window.zenovay('track', eventName, data);
    }
  };

  const identify = (userId: string, traits: Record<string, any> = {}) => {
    if (window.zenovay) {
      window.zenovay('identify', userId, traits);
    }
  };

  return { track, identify };
}

Performance Optimization

Debounce Tracking

Prevent excessive tracking:

import { useZenovay } from '../hooks/useZenovay';
import { useMemo } from 'react';
import { debounce } from 'lodash';

function SearchBox() {
  const { track } = useZenovay();

  const trackSearch = useMemo(
    () =>
      debounce((query: string) => {
        track('search_performed', { query, length: query.length });
      }, 500),
    []
  );

  return (
    <input
      type="search"
      onChange={(e) => trackSearch(e.target.value)}
      placeholder="Search..."
    />
  );
}

Testing

Mock Zenovay in Tests

// test/setup.ts
beforeEach(() => {
  // Mock the global zenovay function
  window.zenovay = jest.fn();
});

Test Component

import { render, fireEvent } from '@testing-library/react';
import { TrackableButton } from './TrackableButton';

beforeEach(() => {
  window.zenovay = jest.fn();
});

test('tracks event on click', () => {
  const { getByText } = render(
    <TrackableButton eventName="button_clicked" eventData={{ id: '123' }}>
      Click Me
    </TrackableButton>
  );

  fireEvent.click(getByText('Click Me'));

  expect(window.zenovay).toHaveBeenCalledWith('track', 'button_clicked', { id: '123' });
});

Next.js App Router Example

Complete integration with Next.js 13+ App Router:

// app/layout.tsx
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <Script
          defer
          data-tracking-code="YOUR_TRACKING_CODE"
          src="https://api.zenovay.com/z.js"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

// app/analytics.tsx
'use client';

import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';

export function Analytics() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    if (window.zenovay) {
      window.zenovay('track', 'pageview', {
        path: pathname,
        title: document.title,
      });
    }
  }, [pathname, searchParams]);

  return null;
}

Environment Variables

# .env.local
# No special environment variables needed for Zenovay!
# The tracking code is embedded directly in the script tag.
# Simply replace YOUR_TRACKING_CODE with your actual tracking code
# from the Zenovay dashboard (app.zenovay.com).

Additional Resources

このページは役に立ちましたか?