Использование OpenReplay с Next.js

Узнайте, как заставить трекер работать в вашем приложении на Next.js

Использование OpenReplay с Next.js

Заставить трекер работать в приложении на Next.js довольно просто. Поскольку Next.js работает поверх React, процесс во многом аналогичен; основная сложность связана с возможностями рендеринга на стороне сервера (Server Side Rendering) в Next.js.

Но как только вы поймёте, как они работают и как структурировать код, предназначенный только для клиента, вы сможете отслеживать своих пользователей в мгновение ока.

Добавление кода отслеживания

Section titled Добавление кода отслеживания

Хотя при создании нового проекта вы можете получить версию кода для копирования и вставки прямо с платформы, этот код слишком примитивен и может не соответствовать передовым практикам, ожидаемым вашей командой или компанией.

Этот метод предлагает минималистичный подход, при котором мы внедряем трекер в приложение в виде компонента, который будет динамически загружать библиотеку.

App Router (v14+) - use client

Section titled App Router (v14+) - use client
'use client'
import { useEffect } from "react"
// note that you can manually import Tracker class if you want to handle the instance manually
import { tracker } from '@openreplay/tracker'

tracker.configure({
  projectKey: PROJECT_KEY,
  ingestPoint: "https://openreplay.mydomain.com/ingest", // when dealing with the self-hosted version of OpenReplay
})

const Openreplay = () => {
  useEffect(() => {
    if (typeof window !== 'undefined') {
      tracker.start()
    }
  }, [])

  return null
}

export default Openreplay 
App Router - динамический импорт экземпляра компонента Openreplay в корневой layout
Section titled App Router - динамический импорт экземпляра компонента Openreplay в корневой layout
import dynamic from "next/dynamic";

const OpenReplayNoSSR = dynamic(() => import('//import the Openreplay instance'), {
  ssr: false, //disables Server-side pre-rendering so window won't be undefined
})

export default function RootLayout({ children }) {
  return (
    <html lang="en" >
      <body>
        {children}
        <OpenReplayNoSSR />
      </body>
    </html >
  );
}

Обычный компонент (Next до версии v13)

Section titled Обычный компонент (Next до версии v13)
[...]

function TrackerComponent() {
const [tracker, setTracker] = React.useState(null);

  React.useEffect(() => {
    (async function () {
      const { tracker } = await import("@openreplay/tracker");
      tracker.configure({
        projectKey: PROJECT_KEY,
        ingestPoint: "https://openreplay.mydomain.com/ingest", // when dealing with the self-hosted version of OpenReplay
        __DISABLE_SECURE_MODE: true,
      });

      setTracker(tracker);
    })();
  }, []);
[...]

этот метод позволяет создать контекст, который, в свою очередь, упрощает управление состоянием трекера и вызов его различных API.

import { createContext } from "react"
import {v4 as uuidV4} from 'uuid'
import { useReducer } from "react"

export const TrackerContext = createContext()

function defaultGetUserId() {
   return uuidV4() 
}

async function newTracker(config) {
    // we use dynamic import to make sure that tracker is started inside browser env
    const { tracker } = await import("@openreplay/tracker");
    const getUserId = (config?.userIdEnabled && config?.getUserId) ? config.getUserId : defaultGetUserId
    let userId = null;

    const trackerConfig = {
        projectKey: config?.projectKey || process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY
    }

    tracker.configure(trackerConfig);
    
    if(config?.userIdEnabled) {
        userId = getUserId()
        tracker.setUserID(userId)
    }
    return tracker

}

function reducer(state, action) {
        switch(action.type) {
            case 'init': {
                if(!state.tracker) {
                    console.log("Instantiaing the tracker for the first time...")
                    return {...state, tracker: newTracker(state.config)}
                }
                return state
            }
            case 'start': {
                console.log("Starting tracker...")
                state.tracker.start()
                return state
            }
        }
    }

export default function TrackerProvider({children, config = {}}) {
    let [state, dispatch] = useReducer(reducer, {tracker: null, config})
    let value = {
        startTracking: () => dispatch({type: 'start'}),
        initTracker: () => dispatch({type: 'init'})
    }

    return <TrackerContext.Provider value={value}>{children}</TrackerContext.Provider>
}

Этот провайдер предоставляет пользователю 2 метода:

  • initTracker: который создаёт экземпляр трекера с предоставленной конфигурацией.
  • startTracking: который запускает процесс отслеживания.

Этот провайдер также принимает объект «конфигурации», который вы можете расширять для будущих возможностей. В этом примере показано, как при необходимости включить возможность однозначной идентификации пользователей. Вы можете использовать UUID по умолчанию, который будет уникальным для каждого пользователя при каждом посещении страницы. В качестве альтернативы вы можете предоставить пользовательскую функцию getUserId в составе конфигурации.

С помощью вашей пользовательской функции вы можете передавать идентификатор клиента вашего магазина, адрес электронной почты пользователя или что-либо ещё, что однозначно идентифицирует пользователя в вашем приложении. Таким образом вы сможете отслеживать повторы их сессий с течением времени на случай, если у них возникнет конкретная проблема, о которой не сообщают другие.

Обработка «projectKey» в вашем коде

Section titled Обработка «projectKey» в вашем коде

Обратите внимание, что настраиваемый вами projectKey — это тот, который предоставляется платформой OpenReplay. По соображениям безопасности это значение не следует хранить жёстко прописанным (hardcoded) в коде, а лучше держать его в каком-либо файле/системе конфигурации.

В этом примере я пользуюсь файлами .env от Next. Что вам нужно понять: если вы не добавите к переменной окружения префикс NEXT_PUBLIC, ваш клиентский код (трекер) не получит к ней доступ.

Если это для вас неприемлемо, вы можете использовать getStaticProps внутри первой страницы, которую посещают ваши пользователи, чтобы получить это значение до создания экземпляра трекера. Оба примера вы можете увидеть ниже.

Использование контекста трекера

Section titled Использование контекста трекера

Импортируйте и оберните ваше приложение в TrackerProvider, который мы создали ранее, вот так:

import '../styles/globals.css'
import TrackerProvider from '../context/trackerContext'

function MyApp({ Component, pageProps }) {

  return <TrackerProvider>
        <Component {...pageProps} />
    </TrackerProvider>
}

export default MyApp

Это файл _app.js, который служит точкой входа для всего приложения.

А затем вы можете запускать отслеживание из любого компонента по вашему желанию, например так:

import { useContext, useEffect } from 'react'
import { TrackerContext } from '../context/trackerContext'
import styles from '../styles/Home.module.css'

export default function YourComponent({projectKey}) {
    
    const {initTracker, startTracking} = useContext(TrackerContext)
    
    useEffect( () => {
       initTracker()
       startTracking()
    }, [])
//.... rest of your code
}

Если вам не нужен такой уровень контроля, вы можете объединить оба сообщения — «сообщение init» и «сообщение start tracking», — чтобы для запуска отслеживания достаточно было вызвать всего одну функцию.

Чтобы посмотреть полный код работающего приложения на основе Next, вы можете заглянуть в этот репозиторий.

Если у вас возникнут какие-либо проблемы с настройкой трекера в вашем проекте на основе Next, свяжитесь с нами в нашем сообществе Slack и задайте вопросы нашим разработчикам напрямую!