NOTE 18 MARET 2023:
- Solusi ini hanya untuk React Navigation versi 5.
- Cek artikel terbaru yang membahas ini di Tracking User Activities with React Navigation Bagian 2
Daftar Isi:
Masalah
Ketika Kita buat aplikasi mobile dan mulai kompleks dengan banyaknya tampilan atau halaman. Maka, Kita perlu tahu halaman apa sih yang sering dikunjungi oleh pengguna aplikasi kita. Dari situ kita bisa jadikan data statistik yang bisa diolah kedepannya nanti selain trafik unduhan. Jika Kita terbiasa di web analytic tools pasti tau kita bisa lihat pengunjung di web Kita mengunjungi halaman apa saja, misalnya lewat google search console. Nah gimana dengan aplikasi mobile. Ok, Stay tuned!
Solusi
Kita perlu buat secara manual namun tidak sepenuhnya manual, hanya perlu buat mekanisme tracking via render props dari react navigation. Selanjutnya, kita simpan ke backend kita sendiri atau menggunakan analytic tool semisal appcenter analytics atau firebase analytics. Ada 2 render props react-navigation yang kita gunakan untuk implementasi ini, yaitu onReady
dan onStateChange
. Ok, lanjut ke implementasi.
Implementasi
Note: kita akan menggunakan react-navigation
v6 dan appcenter-analytics
v4 dan obviously menggunakan TypeScript 👍.
Pertama, Kita buat semacam helper module khusus untuk navigation di aplikasi. katakanlah NavigationHelper.ts
.
import { createNavigationContainerRef, InitialState, NavigationState, PartialState, Route, } from '@react-navigation/native'; export const navigationRef = createNavigationContainerRef(); type MaybeActiveRoute = (Omit<Route<string>, 'key'> & {key?: string; state?: InitialState}) | undefined; // Gets the current screen from navigation state export const getActiveRoute = ( state: NavigationState | PartialState<NavigationState> | undefined, ): MaybeActiveRoute => { // @ts-ignore const route: MaybeActiveRoute = state?.routes[state?.index]; if (route?.state) { // Dive into nested navigators return getActiveRoute(route.state); } return route; }; export const getActiveRouteName = (state: NavigationState | undefined) => getActiveRoute(state)?.name; export const getActiveRouteParams = (state: NavigationState | undefined) => getActiveRoute(state)?.params; export const getPreviousRoute = (state: NavigationState | undefined) => getActiveRoute(state)?.state?.history; export function omitDeep(props: {[key: string]: any}, keys: string[]) { if (!props) { return props; } let obj = {}; for (let [key, val] of Object.entries(props)) { obj[key] = val; if (keys.includes(key)) { delete obj[key]; } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) { obj[key] = omitDeep(val, keys); } } return obj; }
Kode khusus tracking terdiri dari 4 function:
getActiveRoute
, mencari route state react navigation secara recursive, function ini adalah main logic dari function lainnya.getActiveRouteName
, mengambil spesifik nama route state.getActiveRouteParams
, mengambil spesifik params route state.getPreviousRoute
, mengambil spesifik routes sebelumnya dalam bentuk list atau array.
Selanjutnya, Kita buat file Router.tsx
sebagai root navigation kita dan kita implementasikan kode diatas disini.
import {NavigationContainer} from '@react-navigation/native'; import Analytics from 'appcenter-analytics'; import {getActiveRouteName, getActiveRouteParams, navigationRef, omitDeep} from './NavigationHelper'; const omittedKeys = ['password', 'phoneNumber']; export default function Router() { const routeNameRef = React.useRef<string | undefined>(''); const routeParamRef = React.useRef<{[key: string]: any} | undefined>({}); const onReady = () => { if (navigationRef.isReady()) { let navState = navigationRef.getRootState(); routeNameRef.current = getActiveRouteName(navState); routeParamRef.current = getActiveRouteParams(navState); // other codes if you like } }; return ( <NavigationContainer ref={navigationRef} onReady={onReady} onStateChange={(navState) => { const previousRouteName = routeNameRef.current; const previousRouteParams = routeParamRef.current; const currentRouteName = getActiveRouteName(navState); const currentRouteParams = getActiveRouteParams(navState); if (previousRouteName !== currentRouteName) { // if (!__DEV__) { // uncomment this when you only need tracking in release build Analytics.trackEvent('Navigation Change', { screen: currentRouteName ?? 'Root', params: currentRouteParams ? JSON.stringify(omitDeep(currentRouteParams, omittedKeys)) : '', }); } // } // uncomment this when you only need tracking in release build routeNameRef.current = currentRouteName; routeParamRef.current = currentRouteParams; }} > <Navigator /> </NavigationContainer> ); }
Note: <Navigator />
disini adalah Component yang mengimplementasi Stack Navigator dan tidak perlu saya tulis disini.
Mari kita fokus di bagian onReady
dan onStateChange
, onReady
hanya jalan sekali saat pertama kali NavigationContainer
mounted. Kita lakukan inisiasi route yang aktif pertama. Selanjutnya, onStateChange
akan jalan setiap kali pengguna pindah-pindah halaman atau screens, disini ada pengecekan jika route sebelumnya tidak sama dengan route yang baru saja dicek maka dipastikan itu route yang baru. Kemudian Kita simpan ke Appcenter Analytics sekaligus data params. Terakhir, pastikan untuk properti data params yang kita masukkan tidak ada data yang sensitif seperti password
atau phone number
.
Penutup
Saya mau menambahkan sebelum saya tutup, bahwa di iOS itu strict untuk masalah analytics dan tracking maka Kita perlu implementasi app tracking transparency. Untungnya, sudah ada yang buat pustakanya yaitu react-native-tracking-transparency
. Cek sendiri di google atau di ChatGPT 🔍.
Saya rasa cukup, terima kasih atas kesediannya untuk membaca tulisan Saya. Semoga bermanfaat dan apabila bermanfaat, jangan lupa untuk share ke teman-teman developer yang lain. jika ada feedback atau mau kolaborasi jadi guest author di lanjutkoding.com bisa langsung kontak saya di https://t.me/ikhsaan