メインコンテンツへスキップ
無料15 minutes中級

React統合

ZenovayとReactアプリケーションの統合 — トラッキング、フック、コンポーネントベースのアナリティクス。このAPI統合ガイドでReactについて学びましょう。

reactjavascriptintegrationspatracking
最終更新日:

自動ページトラッキング、カスタムイベント、グローバルのwindow.zenovay関数用カスタムフックを使用して、ZenovayアナリティクスをReactアプリケーションに統合します。

インストール

index.htmlにZenovayトラッキングスクリプトを追加します:

<script
  defer
  data-tracking-code="YOUR_TRACKING_CODE"
  src="https://api.zenovay.com/z.js"
></script>

npmパッケージは不要です。スクリプトは自動的にページビューを追跡し、カスタムトラッキング用のグローバルwindow.zenovay関数を提供します。

基本セットアップ

useZenovayフックの作成

グローバルのwindow.zenovay関数をラップするカスタムフックを作成します:

// hooks/useZenovay.js
export function useZenovay() {
  const trackEvent = (name, properties) => {
    if (window.zenovay) {
      window.zenovay('track', name, properties);
    }
  };

  const trackGoal = (name, properties) => {
    if (window.zenovay) {
      window.zenovay('goal', name, properties);
    }
  };

  const trackPageview = () => {
    if (window.zenovay) {
      window.zenovay('page');
    }
  };

  const trackRevenue = (amount, currency, meta) => {
    if (window.zenovay) {
      window.zenovay('revenue', amount, currency, meta);
    }
  };

  const identify = (userId, properties) => {
    if (window.zenovay) {
      window.zenovay('identify', userId, properties);
    }
  };

  return { trackEvent, trackGoal, trackPageview, trackRevenue, identify };
}

自動ページトラッキング

React Routerとの使用

Zenovayスクリプトは最初のページ読み込みを自動的にトラックします。React RouterでのSPAルート変更には、ルート変更時にページビューをトラックします:

import { BrowserRouter, useLocation } from 'react-router-dom';
import { useEffect } from 'react';

function PageTracker() {
  const location = useLocation();

  useEffect(() => {
    if (window.zenovay) {
      window.zenovay('page');
    }
  }, [location.pathname]);

  return null;
}

function App() {
  return (
    <BrowserRouter>
      <PageTracker />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/pricing" element={<Pricing />} />
      </Routes>
    </BrowserRouter>
  );
}

ページビューはルート変更時に自動的にトラックされます。

カスタムイベント

useZenovayフック

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

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

  const handleSignup = () => {
    // サインアップクリックをトラック
    trackEvent('signup_click', {
      plan: 'pro',
      source: 'pricing_page'
    });

    // サインアップロジックを続行
    performSignup();
  };

  return (
    <button onClick={handleSignup}>
      無料トライアルを開始
    </button>
  );
}

トラッキング関数

const { trackEvent } = useZenovay();

// シンプルなイベント
trackEvent('button_click');

// プロパティ付き
trackEvent('purchase', {
  product_id: 'SKU-001',
  value: 99.99,
  currency: 'USD'
});

// 値付き
trackEvent('download', { file: 'guide.pdf' });

ゴール追跡

ゴール完了をトラック

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

function CheckoutSuccess({ order }) {
  const { trackGoal } = useZenovay();

  useEffect(() => {
    trackGoal('purchase', {
      value: order.total,
      order_id: order.id,
      products: order.items
    });
  }, [order]);

  return <div>ご注文ありがとうございます!</div>;
}

ユーザー識別

ユーザーを識別

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

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

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

  return null;
}

認証フロー内

async function handleLogin(credentials) {
  const user = await login(credentials);

  // ログイン後に識別
  if (window.zenovay) {
    window.zenovay('identify', user.id, {
      email: user.email
    });
  }

  navigate('/dashboard');
}

売上トラッキング

売上をトラック

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

function OrderConfirmation({ order }) {
  const { trackRevenue } = useZenovay();

  useEffect(() => {
    trackRevenue(order.total, 'USD', {
      order_id: order.id,
      items: order.items.map(item => ({
        id: item.sku,
        name: item.name,
        price: item.price,
        quantity: item.quantity
      }))
    });
  }, [order]);

  return (
    <div>
      <h1>注文が確定しました!</h1>
      <p>注文番号 #{order.id}</p>
    </div>
  );
}

イベント追跡コンポーネント

トラック可能なボタン

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

function TrackableButton({ event, eventData, children, ...props }) {
  const { trackEvent } = useZenovay();

  const handleClick = (e) => {
    trackEvent(event, eventData);
    props.onClick?.(e);
  };

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

// 使用方法
<TrackableButton
  event="cta_click"
  eventData={{ location: 'hero' }}
  onClick={handleSignup}
>
  はじめる
</TrackableButton>

マウント時にトラック

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

function TrackOnMount({ event, eventData }) {
  const { trackEvent } = useZenovay();

  useEffect(() => {
    trackEvent(event, eventData);
  }, []);

  return null;
}

// 使用方法
<TrackOnMount
  event="pricing_viewed"
  eventData={{ referrer: document.referrer }}
/>

TypeScriptサポート

型定義

// types/zenovay.d.ts
interface ZenovayFunction {
  (command: 'track', name: string, properties?: Record<string, unknown>): void;
  (command: 'identify', userId: string, traits?: Record<string, unknown>): void;
  (command: 'goal', name: string, properties?: Record<string, unknown>): void;
  (command: 'page'): void;
  (command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
}

declare global {
  interface Window {
    zenovay: ZenovayFunction;
  }
}

export {};

型付きフック

// hooks/useZenovay.ts
export function useZenovay() {
  const trackEvent = (name: string, properties?: Record<string, unknown>) => {
    if (window.zenovay) {
      window.zenovay('track', name, properties);
    }
  };

  const trackGoal = (name: string, properties?: Record<string, unknown>) => {
    if (window.zenovay) {
      window.zenovay('goal', name, properties);
    }
  };

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

  return { trackEvent, trackGoal, identify };
}

サーバーサイドレンダリング

SSR問題を回避

window.zenovay関数はブラウザでのみ利用可能です。使用前に必ず確認してください:

import { useEffect, useState } from 'react';

function ZenovayWrapper({ children }) {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  // コンポーネントのマウント後のみトラック(クライアント側)
  if (!mounted) {
    return children;
  }

  return children;
}

テスト

フックをモック

// test-utils.jsx
import { vi } from 'vitest';

export const mockZenovay = {
  trackEvent: vi.fn(),
  trackGoal: vi.fn(),
  identify: vi.fn(),
  trackPageview: vi.fn(),
  trackRevenue: vi.fn()
};

vi.mock('./hooks/useZenovay', () => ({
  useZenovay: () => mockZenovay
}));

テスト例

import { render, fireEvent, screen } from '@testing-library/react';
import { mockZenovay } from './test-utils';
import SignupButton from './SignupButton';

test('サインアップクリックをトラック', () => {
  render(<SignupButton />);

  fireEvent.click(screen.getByText('無料トライアルを開始'));

  expect(mockZenovay.trackEvent).toHaveBeenCalledWith('signup_click', expect.any(Object));
});

一般的なパターン

Eコマーストラッキング

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

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

  useEffect(() => {
    trackEvent('product_viewed', {
      product_id: product.id,
      product_name: product.name,
      price: product.price,
      category: product.category
    });
  }, [product.id]);

  const handleAddToCart = () => {
    trackEvent('add_to_cart', {
      product_id: product.id,
      price: product.price,
      quantity: 1
    });
  };

  return (
    <div>
      <h1>{product.name}</h1>
      <button onClick={handleAddToCart}>カートに追加</button>
    </div>
  );
}

フォームトラッキング

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

function ContactForm() {
  const { trackEvent, trackGoal } = useZenovay();

  const handleSubmit = async (data) => {
    trackEvent('form_submitted', {
      form_name: 'contact',
      has_phone: !!data.phone
    });

    await submitForm(data);

    trackGoal('contact_form');
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* フォームフィールド */}
    </form>
  );
}

トラブルシューティング

イベントがトラックされない

確認してください:

  • スクリプトタグがindex.htmlにあるか
  • トラッキングコードが正しいか
  • 広告ブロッカーにブロックされていないか
  • コンソールにエラーがないか

ページビューが欠落している

確認してください:

  • PageTrackerコンポーネントがマウントされているか
  • React Routerが正しく設定されているか
  • ルートがルーター内でレンダリングされているか

重複トラッキング

回避してください:

  • 複数のスクリプトタグ
  • 手動 + 自動トラッキング
  • StrictModeによるダブルエフェクト

次のステップ

この記事は役に立ちましたか?