استخدام الأحداث المخصصة لتحسين التتبّع لديك

التقط جميع أنواع الأحداث المخصصة عبر واجهة برمجة تطبيقات الأحداث المخصصة

استخدام الأحداث المخصصة لتحسين التتبّع لديك

الأحداث المخصصة مفهوم بسيط لكنه قوي يوفّره الـ Tracker الخاص بنا دون الحاجة إلى إضافة أي شيء إضافي. يمكنها توسيع البيانات المُتتبَّعة بأي شيء آخر تحتاج إليه، سواء كانت أخطاء مخصصة متعلقة بمنطق عملك أو حتى أحداثًا بسيطة، لتكون على دراية بما يفعله مستخدموك.

سيتتبّع الـ tracker الخاص بنا، افتراضيًا، العديد من الأشياء المختلفة، بما في ذلك بعض الأخطاء المفيدة، لكنها قد لا تكون كافية بالنسبة لك، ولهذا السبب لدينا الأحداث المخصصة.

إضافة الأحداث المخصصة

Section titled إضافة الأحداث المخصصة

في هذا المثال، لنأخذ موقع تجارة إلكترونية عامًّا ونضِف بعض الأحداث لفهم متى يضيف مستخدمنا منتجًا إلى عربة التسوّق.

افتراضيًا، لن يتتبّع OpenReplay تلك المعلومات. ومع ذلك، من خلال الأحداث المخصصة، يمكنك تتبّع هذا بسهولة.

كل ما تحتاج إلى معرفته الآن هو أن هذا مزوّد سياق (context provider) نقوم بإنشائه، وسيتيح لك التفاعل مع الـ tracker عبر عدّة دوال.

على وجه الخصوص، سنهتم بـ logIssue وlogEvent اللذين يتيحان لك إرسال مشكلة أو حدث مخصص إلى المنصّة.

  • الأحداث (Events) مُخصّصة لتسجيل الإجراءات الخاصة بالمستخدم. في حالتنا، على سبيل المثال، سنسجّل إضافة منتج إلى العربة.
  • المشكلات (Issues)، من ناحية أخرى، مُخصّصة لتسجيل الأخطاء التي لا يلتقطها الـ tracker الخاص بنا تلقائيًا. في حالتنا، سنحاكي خطأ شبكة يمنعنا من الوصول إلى واجهة برمجة تطبيقات تابعة لجهة خارجية. وعندما يحدث ذلك، سنسجّل مشكلة على المنصّة.
import { createContext, useCallback } from 'react'
import Tracker from '@openreplay/tracker'
import { v4 as uuidV4 } from 'uuid'
import { useReducer } from 'react'

export const TrackerContext = createContext()
function defaultGetUserId() {
  return uuidV4()
}
function newTracker(config) {
  const getUserId =
    config?.userIdEnabled && config?.getUserId
      ? config.getUserId
      : defaultGetUserId
  let userId = null
  const trackerConfig = {
    projectKey:
      config?.projectKey || process.env.NEXT_PUBLIC_OPENREPLAY_PROJECT_KEY,
 
  }
  if (config?.ingestPoint || process.env.NEXT_PUBLIC_OPENREPLAY_INGEST_POINT) {
    trackerConfig.ingestPoint =
      config?.ingestPoint || process.env.NEXT_PUBLIC_OPENREPLAY_INGEST_POINT
  }

  console.log('Tracker configuration: ')
  console.log(trackerConfig)
  const tracker = new Tracker(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...')
        let t = newTracker(state.config)
        let pluginsReturnedValue = {}
        if (state.config.plugins) {
          state.config.plugins.forEach((p) => {
            console.log('Using plugin...')
            pluginsReturnedValue[p.name] = t.use(p.fn(p.config))
          })
        }
        return {
          ...state,
          pluginsReturnedValue: pluginsReturnedValue,
          tracker: t,
        }
      }
      return state
    }
    case 'start': {
      console.log('Starting tracker...')
      state.tracker.start()
      return state
    }
    case 'logEvent': {
      console.log('Logging event')
      state.tracker?.event(action.payload?.name, action.payload?.data)
      return state
    }
    case 'logIssue': {
      console.log('Logging issue')
      state.tracker?.issue(action.payload?.name, action.payload?.data)
      return state
    }
  }
}
export default function TrackerProvider({ children, config = {} }) {
  let [state, dispatch] = useReducer(reducer, {
    tracker: null,
    pluginsReturnedValue: {},
    config,
  })
  let value = {
    startTracking: () => dispatch({ type: 'start' }),
    initTracker: () => dispatch({ type: 'init' }),
    logEvent: (evnt) => dispatch({ type: 'logEvent', payload: evnt }),
    logIssue: (evnt) => dispatch({ type: 'logIssue', payload: evnt }),
    pluginsReturnedValues: { ...state.pluginsReturnedValue },
  }
  return (
    <TrackerContext.Provider value={value}>{children}</TrackerContext.Provider>
  )
}

تمتلك الدالّتان logEvent وlogIssue التوقيع نفسه؛ سنمرّر كائنًا يحتوي على الخاصيتين name وdata. سيُستخدَم name لتعريف سجلّنا في واجهة OpenReplay، بينما سيحتوي data على المعلومات المُسجَّلة.

تذكّر: يجب أن تحتوي الخاصية data على كائن قابل للتسلسل (serializable).

بعد ذلك يمكننا إعداد هذا المزوّد في ملفّ _app.tsx لدينا على النحو التالي:


//imports here...

export default function MyApp({ Component, pageProps }: AppProps) {
  const Layout = (Component as any).Layout || Noop

  useEffect(() => {
    document.body.classList?.remove('loading')
  }, [])

  return (
    <TrackerProvider config={{}}>
      <Head />
      <ManagedUIContext>
        <Layout pageProps={pageProps}>
          <Component {...pageProps} />
        </Layout>
      </ManagedUIContext>
    </TrackerProvider>
  )
}

بعد الانتهاء من ذلك، يمكننا الآن الانتقال إلى تشغيل الأحداث.

تسجيل المشكلات والأحداث المخصصة

Section titled تسجيل المشكلات والأحداث المخصصة

من أجل ذلك، سنستفيد من واجهتنا:

صفحة المنتج

سنسجّل حدثًا جديدًا في كل مرة يضيف فيها المستخدم منتجًا إلى عربتنا (أي عندما يضغط على زرّ “ADD TO CART”).

وسنسجّل مشكلة إذا فعل ذلك من دون اختيار مقاس أولًا.

يمكنك الاطّلاع على الكود المصدري الكامل لهذا المكوّن من هنا مباشرةً، لكن لنركّز على المنطق الذي سنضيفه.

في بداية مكوّننا سنستخدم الـ hook المسمّى useContext:

//outside the component
import { TrackerContext } from '../../../context/trackerProvider'

//inside the component
const { logEvent, logIssue } = useContext(TrackerContext)

داخل الدالّة addToCart، سنضيف المنطق التالي للتحقّق من عدم اختيار مقاس صالح:

const validSizes = product.options
      .filter((o) => o.id == 'option-size')
      .map((o) => o.values)[0]

    let pickedSized = validSizes.find((s) => {
      return selectedOptions.size == s.label.toLowerCase()
    })

    if (!pickedSized) {
      logIssue({
        name: 'Product added without a size',
        data: {
          product_id: product.id,
          added_date: new Date(),
          available_options: validSizes,
        },
      })
    }

الجزء الأساسي من الكود هو الـ IF الأخير: فعندما ندرك أنه لم يُختَر مقاس صالح، نستدعي الدالّة logIssue التي حصلنا عليها من استدعاء useContext السابق.

أما بالنسبة إلى الحدث، فسنتعمّق أكثر في الدالّة نفسها وسنضيف:

logEvent({
        name: 'product_added',
        data: {
          id: product.id,
          date_added: new Date(),
        },
      })

هذا كل ما نحتاج إليه؛ بعد ذلك يمكننا الانتقال إلى OpenReplay، والعثور على تسجيل الجلسة الخاص بنا، وفحص قسم Events:

لوحة الأحداث المخصصة

وإذا أردنا الاطّلاع على التفاصيل، يمكننا النقر على رابط “DETAILS” الذي يظهر عند تمرير المؤشر فوق أحد الصفوف:

تفاصيل حدث مخصص

تلك هي التفاصيل التي سجّلناها عند إضافة منتج.

وإذا كنت ترغب في مراجعة الكود المصدري الكامل لهذا المثال، يمكنك أن تجده هنا.

إذا واجهت أي مشكلات مع الأحداث المخصصة في مشروعك، فيُرجى التواصل معنا عبر مجتمعنا على Slack وطرح أسئلتك على مطوّرينا مباشرةً!