Олег Марков
Сегмент hooks - библиотека hooks-segment для интеграции Segment в React
Введение
Сегмент hooks (или библиотека hooks-segment) — это удобный способ интеграции аналитической платформы Segment в React‑приложения через хуки. Вместо того чтобы напрямую обращаться к глобальному объекту analytics, вы можете использовать понятные React-хуки, которые лучше вписываются в архитектуру современного фронтенда.
Задача этой статьи — показать, как с помощью hooks-segment:
- инициализировать Segment в React-приложении;
- отправлять события;
- отслеживать просмотры страниц (screen / page view);
- работать с пользователем (identify, group, alias);
- удобно пользоваться типизацией и контекстом.
Смотрите, я покажу вам, как это работает по шагам: от базовой интеграции до продвинутых сценариев.
Что такое hooks-segment и зачем он нужен
Кратко о Segment
Segment — это платформа для сбора и маршрутизации аналитических данных. Основная идея:
- вы отправляете события в единый SDK Segment;
- Segment перенаправляет эти данные в десятки интеграций: Amplitude, Mixpanel, GA4, BI-системы и т. д.;
- вам не нужно встраивать каждую аналитическую систему отдельно.
В вебе Segment обычно использует JavaScript SDK с глобальным объектом analytics:
analytics.track('Product Viewed', { productId: '123' })
Но такой подход не всегда удобно использовать в React:
- нет явной зависимости в коде (глобальная переменная);
- сложно тестировать;
- сложно типизировать;
- неудобно использовать в хук-ориентированных архитектурах.
Что дает hooks-segment
hooks-segment оборачивает Segment API в React-контекст и хуки. Обычно библиотека предоставляет:
- провайдер, который инициализирует Segment и прокидывает его в контекст;
- хук для получения инстанса Segment (
useSegment/useSegmentAnalytics); - специализированные хуки для:
- отправки событий (
track); - идентификации пользователя (
identify); - отслеживания страниц (
page/screen); - группировки (
group); - связывания анонимного и авторизованного пользователя (
alias).
- отправки событий (
В итоге вы получаете:
- явную зависимость — хуки импортируются из библиотеки;
- лучшую тестируемость — контекст можно замокать;
- поддержку SSR/Next.js благодаря контролируемой инициализации;
- более чистый и предсказуемый код.
Установка и базовая конфигурация hooks-segment
Установка библиотеки
Обычно пакет устанавливается через npm или yarn. Конкретное имя может отличаться, но для статьи будем использовать условное название hooks-segment:
npm install hooks-segment
# или
yarn add hooks-segment
Если вы используете TypeScript, убедитесь, что у пакета есть типы. Чаще всего они уже включены, но иногда придется установить @types/segment-analytics.
Инициализация Segment через провайдер
Главная идея — обернуть все приложение в провайдер, который инициализирует Segment и хранит инстанс SDK в React-контексте.
Давайте разберемся на примере:
// src/analytics/SegmentProvider.tsx
import React from 'react'
import { SegmentProvider } from 'hooks-segment'
// Здесь мы передаем настройки Segment в провайдер
const segmentWriteKey = process.env.REACT_APP_SEGMENT_WRITE_KEY as string
export const AppSegmentProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return (
<SegmentProvider writeKey={segmentWriteKey} options={{ debug: false }}>
{/* Внутри провайдера будет доступ к хукам hooks-segment */}
{children}
</SegmentProvider>
)
}
Теперь оборачиваем приложение:
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { AppSegmentProvider } from './analytics/SegmentProvider'
import { App } from './App'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
<React.StrictMode>
{/* Здесь мы оборачиваем все приложение в AppSegmentProvider */}
<AppSegmentProvider>
<App />
</AppSegmentProvider>
</React.StrictMode>
)
Комментарий к коду:
SegmentProviderвнутри инициализирует Segment с вашимwriteKey;- все дочерние компоненты могут вызывать хуки из hooks-segment;
- вы централизуете конфигурацию и исключаете дублирование.
Если вы используете Next.js, провайдер лучше подключать в _app.tsx или layout.tsx (для нового App Router).
Основные хуки hooks-segment
В этом разделе вы увидите, как пользоваться ключевыми хуками. Названия могут немного отличаться в конкретной реализации, но сама структура обычно похожа.
Получение инстанса Segment — useSegment
Многие библиотеки предоставляют хук общего вида useSegment (или useSegmentAnalytics), который возвращает объект с методами Segment: track, identify, page, group и т. д.
Покажу вам, как это реализовано на практике:
// src/hooks/useAnalytics.ts
import { useSegment } from 'hooks-segment'
// Этот хук просто реэкспортирует функционал Segment
export const useAnalytics = () => {
const analytics = useSegment()
// Здесь мы можем добавить свои обертки, если нужно
return analytics
}
Пример использования:
// src/components/ButtonBuy.tsx
import React from 'react'
import { useAnalytics } from '../hooks/useAnalytics'
interface ButtonBuyProps {
productId: string
}
export const ButtonBuy: React.FC<ButtonBuyProps> = ({ productId }) => {
const analytics = useAnalytics()
const handleClick = () => {
// Здесь мы отправляем событие "Product Added" в Segment
analytics.track('Product Added', {
productId,
source: 'product_page',
})
}
return (
<button onClick={handleClick}>
Добавить в корзину
</button>
)
}
Обратите внимание, как этот фрагмент кода решает задачу:
- компонент не знает о глобальном объекте
analytics; - мы можем замокать
useAnalyticsв тестах; - события отправляются только внутри React-контекста.
Отправка событий — track
Метод track — один из основных. Он отправляет в Segment событие с названием и опциональными свойствами.
Теория:
event— строка, отражающая действие пользователя:Product Viewed,Checkout Started;properties— объект с данными о событии (идентификаторы, цены, категории);- события лучше стандартизировать и документировать — это облегчает аналитику.
Теперь вы увидите, как это выглядит в коде:
// src/analytics/useTrack.ts
import { useSegment } from 'hooks-segment'
export const useTrack = () => {
const analytics = useSegment()
const trackProductViewed = (params: { productId: string; category?: string }) => {
// Здесь мы используем track для конкретного бизнес-события
analytics.track('Product Viewed', {
productId: params.productId,
category: params.category,
})
}
const trackAddToCart = (params: { productId: string; price: number }) => {
analytics.track('Product Added To Cart', {
productId: params.productId,
price: params.price,
currency: 'USD',
})
}
// Возвращаем набор доменных трекеров
return { trackProductViewed, trackAddToCart }
}
Используем в компоненте:
// src/components/ProductCard.tsx
import React, { useEffect } from 'react'
import { useTrack } from '../analytics/useTrack'
interface ProductCardProps {
productId: string
category?: string
price: number
}
export const ProductCard: React.FC<ProductCardProps> = ({ productId, category, price }) => {
const { trackProductViewed, trackAddToCart } = useTrack()
useEffect(() => {
// Здесь мы отслеживаем просмотр карточки товара
trackProductViewed({ productId, category })
}, [productId, category, trackProductViewed])
const handleAddToCart = () => {
// Здесь мы отслеживаем добавление товара в корзину
trackAddToCart({ productId, price })
}
return (
<div>
<button onClick={handleAddToCart}>
В корзину
</button>
</div>
)
}
Так вы выносите сырые вызовы analytics.track из компонентов в отдельный слой аналитики. Это облегчает рефакторинг и изменение схемы событий.
Идентификация пользователя — identify
identify связывает действия с конкретным пользователем. Обычно вызывается после:
- регистрации;
- логина;
- получения профиля с backend.
Как работает теория:
userId— стабильный идентификатор пользователя (обычно из БД);traits— свойства пользователя (email, тарифный план, роль, страна).
Давайте посмотрим, что происходит в следующем примере:
// src/analytics/useIdentify.ts
import { useSegment } from 'hooks-segment'
export const useIdentify = () => {
const analytics = useSegment()
const identifyUser = (params: {
userId: string
email?: string
name?: string
plan?: string
}) => {
// Здесь мы связываем текущие события с конкретным userId
analytics.identify(params.userId, {
email: params.email,
name: params.name,
plan: params.plan,
})
}
return { identifyUser }
}
Использование после логина:
// src/components/LoginForm.tsx
import React from 'react'
import { useIdentify } from '../analytics/useIdentify'
export const LoginForm: React.FC = () => {
const { identifyUser } = useIdentify()
const handleLoginSuccess = (user: { id: string; email: string; name: string }) => {
// Здесь мы вызываем identify, когда backend подтвердил логин
identifyUser({
userId: user.id,
email: user.email,
name: user.name,
plan: 'free',
})
}
// Здесь может быть реальная логика логина
return (
<form>
{/* Поля ввода опущены для краткости */}
<button type="submit">
Войти
</button>
</form>
)
}
Важно:
- вызывать
identifyтолько после успешной аутентификации; - не использовать временный или локальный ID для
userId; - не передавать чувствительные данные в
traits.
Отслеживание экранов и страниц — page / screen
В веб-приложениях Segment использует метод page, в мобильных — screen. Многие React-приложения используют роутинг, и нам нужно автоматически отправлять события при смене страницы.
Предположим, библиотека hooks-segment предоставляет хук usePage. Если такого хука нет, вы можете использовать useSegment и самостоятельно вызывать analytics.page.
Смотрите, я покажу вам, как это работает с React Router:
// src/analytics/usePageTracking.ts
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { useSegment } from 'hooks-segment'
export const usePageTracking = () => {
const location = useLocation()
const analytics = useSegment()
useEffect(() => {
// Здесь мы вызываем page при каждом изменении пути
analytics.page(undefined, undefined, {
path: location.pathname,
search: location.search,
})
}, [location.pathname, location.search, analytics])
}
Теперь подключаем этот хук в верхнем уровне приложения:
// src/App.tsx
import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { usePageTracking } from './analytics/usePageTracking'
import { HomePage } from './pages/HomePage'
import { ProductPage } from './pages/ProductPage'
export const App: React.FC = () => {
// Здесь мы активируем глобальное отслеживание страниц
usePageTracking()
return (
<BrowserRouter>
<Routes>
{/* Здесь настраиваем маршруты приложения */}
<Route path="/" element={<HomePage />} />
<Route path="/product/:id" element={<ProductPage />} />
</Routes>
</BrowserRouter>
)
}
Комментарии:
usePageTracking— чистый хук, который не зависит от UI;- он срабатывает при каждом изменении
location, отправляя событиеpage; - в
propertiesможно добавить больше информации:title,category,referrer.
Если вы работаете с React Native или мобильным окружением, вместо URL можно использовать названия экранов и параметры навигации.
Группы и alias — group и alias
Группировка пользователей — group
Метод group объединяет пользователей в сущности вроде:
- организация;
- команда;
- компания;
- подписка.
Это полезно для B2B-сценариев, когда аналитика строится по компаниям, а не только по отдельным пользователям.
Пример:
// src/analytics/useGroup.ts
import { useSegment } from 'hooks-segment'
export const useGroup = () => {
const analytics = useSegment()
const groupUserToCompany = (params: {
groupId: string
name: string
plan?: string
}) => {
// Здесь мы связываем пользователя с конкретной группой (компанией)
analytics.group(params.groupId, {
name: params.name,
plan: params.plan,
})
}
return { groupUserToCompany }
}
Вызывается, например, после выбора организации или при логине в multi-tenant‑системе.
alias — связывание анонимного и авторизованного пользователя
alias нужен для связывания:
- анонимных событий (до регистрации);
- с авторизованным пользователем (после регистрации).
В браузере Segment обычно автоматически генерирует anonymousId. Когда пользователь регистрируется, вы хотите, чтобы его pre-signup действия сохранились и были связаны с его новым userId.
Давайте разберемся на примере:
// src/analytics/useAlias.ts
import { useSegment } from 'hooks-segment'
export const useAlias = () => {
const analytics = useSegment()
const aliasUser = (newUserId: string) => {
// Здесь мы связываем существующую анонимную личность с новым UserID
analytics.alias(newUserId)
}
return { aliasUser }
}
Шаблонный сценарий:
- Пользователь выполняет действия как гость — вы вызываете
track,page, но безidentify. - Пользователь регистрируется.
- Вы вызываете
alias(newUserId). - Вызываете
identify(newUserId, traits...).
Это позволяет аналитике корректно объединить историю событий.
Архитектура и лучшие практики интеграции hooks-segment
Разделение доменного уровня и SDK
Вместо того чтобы вызывать useSegment в каждом компоненте, есть смысл:
- создать слой аналитических хуков/сервисов;
- внутри этого слоя использовать объекты Segment;
- наружу отдавать только бизнес-события.
Обратите внимание, как это выглядит:
// src/analytics/events.ts
export enum AnalyticsEventName {
ProductViewed = 'Product Viewed',
ProductAddedToCart = 'Product Added To Cart',
CheckoutStarted = 'Checkout Started',
}
// src/analytics/useShopAnalytics.ts
import { useSegment } from 'hooks-segment'
import { AnalyticsEventName } from './events'
export const useShopAnalytics = () => {
const analytics = useSegment()
const trackProductViewed = (productId: string) => {
analytics.track(AnalyticsEventName.ProductViewed, { productId })
}
const trackAddToCart = (productId: string, price: number) => {
analytics.track(AnalyticsEventName.ProductAddedToCart, {
productId,
price,
})
}
const trackCheckoutStarted = (cartId: string, amount: number) => {
analytics.track(AnalyticsEventName.CheckoutStarted, {
cartId,
amount,
})
}
return {
trackProductViewed,
trackAddToCart,
trackCheckoutStarted,
}
}
Использование в UI:
// src/components/CartButton.tsx
import React from 'react'
import { useShopAnalytics } from '../analytics/useShopAnalytics'
export const CartButton: React.FC = () => {
const { trackCheckoutStarted } = useShopAnalytics()
const handleClick = () => {
// Здесь мы отправляем событие старта оформления заказа
trackCheckoutStarted('cart-123', 99.9)
}
return (
<button onClick={handleClick}>
Оформить заказ
</button>
)
}
Преимущества:
- все события сконцентрированы в одном месте;
- легче поддерживать схемы и проверять, что события соответствуют требованиям продукта;
- меньше связности между компонентами и Segment SDK.
Работа с TypeScript
hooks-segment в связке с TypeScript позволяет сделать трекинг более безопасным.
Подход:
- Заводим типы для событий и их свойств.
- Пишем обертку над
track, которая принимает только известные события.
Пример:
// src/analytics/types.ts
export type AnalyticsEvent =
| {
name: 'Product Viewed'
properties: { productId: string; category?: string }
}
| {
name: 'Product Added To Cart'
properties: { productId: string; price: number }
}
| {
name: 'Checkout Started'
properties: { cartId: string; amount: number }
}
// src/analytics/useTypedTrack.ts
import { useSegment } from 'hooks-segment'
import { AnalyticsEvent } from './types'
export const useTypedTrack = () => {
const analytics = useSegment()
const trackEvent = (event: AnalyticsEvent) => {
// Здесь TypeScript проверяет соответствие имени события и свойств
analytics.track(event.name, event.properties)
}
return { trackEvent }
}
Использование:
// src/components/TypedExample.tsx
import React from 'react'
import { useTypedTrack } from '../analytics/useTypedTrack'
export const TypedExample: React.FC = () => {
const { trackEvent } = useTypedTrack()
const handleClick = () => {
// Здесь мы создаем строго типизированное событие
trackEvent({
name: 'Product Added To Cart',
properties: {
productId: '123',
price: 42,
},
})
}
return (
<button onClick={handleClick}>
Добавить с типами
</button>
)
}
Как видите, этот код выполняет:
- статическую проверку свойств события;
- защиту от опечаток в имени события;
- улучшение документации по событиям прямо в коде.
Состояние загрузки и ошибки инициализации Segment
Иногда Segment может загружаться не сразу:
- медленный интернет;
- блокировщики рекламы;
- проблемы с конфигурацией.
При использовании hooks-segment часто доступен статус:
isReadyилиloaded;- возможно, информация об ошибке.
Пример условного API:
// src/analytics/useSafeSegment.ts
import { useSegment } from 'hooks-segment'
export const useSafeSegment = () => {
const { analytics, isReady, error } = useSegment()
// Здесь мы возвращаем объект, который можно безопасно использовать в компонентах
return {
analytics,
isReady,
error,
}
}
Использование:
// src/components/SafeButton.tsx
import React from 'react'
import { useSafeSegment } from '../analytics/useSafeSegment'
export const SafeButton: React.FC = () => {
const { analytics, isReady } = useSafeSegment()
const handleClick = () => {
// Здесь проверяем, готов ли Segment, прежде чем отправлять событие
if (!isReady) return
analytics.track('Button Clicked', { label: 'SafeButton' })
}
return (
<button onClick={handleClick} disabled={!isReady}>
Клик с безопасной аналитикой
</button>
)
}
Так вы избегаете ошибок, когда analytics еще не инициализирован.
SSR и Next.js особенности
В проектах на Next.js (особенно с SSR) важно учитывать:
- код Segment должен выполняться только в браузере;
- на сервере глобальный объект
windowнедоступен.
Типичный подход:
- Инициализировать Segment только после монтирования на клиенте.
- Отключать трекинг на сервере.
Покажу упрощенный пример:
// src/analytics/SegmentProvider.tsx
import React, { useEffect, useState } from 'react'
import { SegmentProvider as BaseSegmentProvider } from 'hooks-segment'
const writeKey = process.env.NEXT_PUBLIC_SEGMENT_WRITE_KEY as string
export const SegmentProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [isClient, setIsClient] = useState(false)
useEffect(() => {
// Здесь мы отмечаем, что код выполняется в браузере
setIsClient(true)
}, [])
if (!isClient) {
// На сервере и до монтирования мы не инициализируем Segment
return <>{children}</>
}
return (
<BaseSegmentProvider writeKey={writeKey}>
{children}
</BaseSegmentProvider>
)
}
Такой подход позволяет:
- избежать ошибок доступа к
windowна сервере; - подключить Segment только там, где он действительно нужен — в браузере.
Отключение трекинга в режиме разработки и для сотрудников
Частая задача — не портить аналитику:
- локальными запусками;
- тестированием;
- действиями сотрудников компании.
hooks-segment обычно позволяет отключить или переопределить поведение при инициализации.
Один из подходов:
// src/analytics/SegmentProvider.tsx
import React from 'react'
import { SegmentProvider as BaseSegmentProvider } from 'hooks-segment'
const writeKey = process.env.REACT_APP_SEGMENT_WRITE_KEY as string
const isTrackingEnabled = process.env.NODE_ENV === 'production'
export const SegmentProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
if (!isTrackingEnabled) {
// Если трекинг выключен, можно вернуть провайдер-заглушку
// или просто не инициализировать реальный Segment
return <>{children}</>
}
return (
<BaseSegmentProvider writeKey={writeKey}>
{children}
</BaseSegmentProvider>
)
}
Дополнительно:
- можно проверять email пользователя и не отслеживать внутренних сотрудников;
- можно передавать флаг
optOutв Segment и использовать встроенные механизмы.
Заключение
hooks-segment делает интеграцию Segment с React-приложениями более предсказуемой и удобной. Вместо прямого обращения к глобальному analytics вы работаете с хуками и контекстом, что:
- упрощает типизацию и тестирование;
- позволяет централизовать логику аналитики;
- лучше вписывается в архитектуру современных фронтенд-приложений.
Основные шаги работы с Сегмент hooks:
- Установить библиотеку и обернуть приложение в
SegmentProvider. - Использовать общий хук (
useSegment) для доступа к методамtrack,identify,page,group,alias. - Построить над этим доменный слой:
useShopAnalytics,useUserAnalyticsи другие. - Настроить автоматическое отслеживание страниц и важных пользовательских действий.
- Учитывать особенности SSR и окружения (отключение трекинга в dev, обработка ошибок загрузки).
Если вы будете относиться к аналитике как к отдельному слою приложения, а не к случайным вызовам в компонентах, hooks-segment как раз поможет организовать этот слой аккуратно и прозрачно.
Частозадаваемые технические вопросы и ответы
Как замокать hooks-segment в unit-тестах React-компонентов
Обычно достаточно замокать хук или провайдера. Пример с Jest:
// Здесь мы замещаем реальную библиотеку заглушкой
jest.mock('hooks-segment', () => ({
useSegment: () => ({
track: jest.fn(),
identify: jest.fn(),
page: jest.fn(),
}),
}))
Дальше вы рендерите компонент и проверяете, вызывался ли track с нужными аргументами. Важно не забыть сбрасывать моки между тестами.
Как отложить инициализацию Segment до согласия с cookies
Можно хранить состояние согласия в приложении и инициализировать SegmentProvider только после согласия:
// Если пользователь не дал согласие - не рендерим провайдер
return hasConsent ? (
<SegmentProvider writeKey={writeKey}>{children}</SegmentProvider>
) : (
<>{children}</>
)
Пока согласия нет, события не отправляются. После согласия вы можете один раз инициализировать Segment.
Что делать, если браузер блокирует Segment-скрипты
В таких случаях analytics может не инициализироваться. Нужно:
- Обрабатывать состояние
isReadyили аналогичный флаг из hooks-segment. - Не вызывать методы трекинга до готовности.
- Логировать ошибки в fallback-систему (например, Sentry), если SDK сообщает об ошибке.
Так вы избежите падений приложения и сможете видеть долю пользователей с блокировщиками.
Как добавить кастомный транспорт для отправки данных (например через proxy)
Если библиотека hooks-segment позволяет передавать options при инициализации, используйте их:
<SegmentProvider
writeKey={writeKey}
options={{
apiHost: 'https://analytics-proxy.mycompany.com',
}}
>
{children}
</SegmentProvider>
Дальше прокси пересылает запросы в реальный Segment. Это помогает обойти ограничения сетевой инфраструктуры.
Как мигрировать с прямого использования window.analytics на hooks-segment
Шаги:
- Ввести
SegmentProviderи подключить его в корне. - Написать обертку
useAnalytics, которая сначала внутри используетwindow.analytics. - Постепенно заменить прямые обращения к
window.analyticsнаuseAnalytics. - После этого переключить реализацию
useAnalyticsна hooks-segment. - Удалить старый код, использующий глобальный объект.
Так вы переходите к hooks-segment без одномоментного переписывания всего проекта.