@sophonz/react-native-navigation
v0.1.0
Published
A React Native wrapper for the OTel Navigation instrumentation library that creates telemetry data representing Navigation changes
Maintainers
Readme
@sophonz/react-native-navigation
[!IMPORTANT]
This module requires both the React Native Sophonz SDK and the React Native Sophonz Tracer Provider.
This package uses Sophonz's React Native SDK and OpenTelemetry Tracer Provider to collect telemetry around Navigation based on expo-router, @react-navigation/native and react-native-navigation.
Install the component
npm:
npm install @sophonz/react-native-navigationyarn:
yarn add @sophonz/react-native-navigationSetup in your code
Using expo-router:
import React from 'react';
import {useSophonzNativeTracerProvider} from "@sophonz/react-native-tracer-provider";
import {SophonzNavigationTracker} from "@sophonz/react-native-navigation";
import {Stack, useNavigationContainerRef} from 'expo-router';
import {useSophonz} from "@sophonz/react-native";
const App = () => {
const {isStarted} = useSophonz({
ios: {
collectorUrl: "https://in.sophonz.ai",
appKey: "sk_...",
}
});
// The provider is something you need to configure and pass down as prop into the `<SophonzNavigationTracker />` component
// If your choice is not to pass any custom tracer provider, the component will use the global one.
// In both cases you have to make sure a tracer provider is registered BEFORE you attempt to record the first span (otherwise some initial telemetry can be missed).
const {tracerProvider, isLoading: isLoadingTracerProvider} =
useSophonzNativeTracerProvider({}, isStarted);
// If you do not use `expo-router` the same hook is also available in `@react-navigation/native` since `expo-router` is built on top of it.
// Make sure this ref is passed also to the navigation container at the root of your app (if not, the ref would be empty and you will get a console.warn message instead).
const expoNavigationRef = useNavigationContainerRef();
if (isLoadingTracerProvider || tracerProvider === null) {
return (
<View>
<View>
<Text>Loading Tracer Provider...</Text>
</View>
</View>
);
}
return (
<SophonzNavigationTracker
ref={expoNavigationRef}
tracerProvider={tracerProvider}
// These static attributes will be passed into each created span
screenAttributes={{
"static.attribute": 123456,
"custom.key": "abcd...",
}}>
<Stack>
<Stack.Screen name="(tabs)" options={{headerShown: false}} />
<Stack.Screen name="+not-found" />
... rest of stack
</Stack>
</SophonzNavigationTracker>
);
};
export default App;If you are using purely @react-navigation/native:
import React from 'react';
import {useSophonzNativeTracerProvider} from "@sophonz/react-native-tracer-provider";
import {SophonzNavigationTracker} from "@sophonz/react-native-navigation";
import {
NavigationContainer,
useNavigationContainerRef,
} from "@react-navigation/native";
import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
import {useSophonz} from "@sophonz/react-native";
import CartPage from "screens/CartPage";
import CheckoutPage from "screens/CheckoutPage";
const Tab = createBottomTabNavigator();
const App = () => {
const {isStarted} = useSophonz({
ios: {
collectorUrl: "https://in.sophonz.ai",
appKey: "sk_...",
}
});
// The provider is something you need to configure and pass down as prop into the `SophonzNavigationTracker` component
// If your choice is not to pass any custom tracer provider, the <SophonzNavigationTracker /> component will use the global one.
// In both cases you have to make sure a tracer provider is registered BEFORE you attempt to record the first span.
const {tracerProvider, isLoading: isLoadingTracerProvider} =
useSophonzNativeTracerProvider({}, isStarted);
// Tip! as of now if you inspect the source code of `useNavigationContainerRef` from `@react-navigation/native` you will see that it returns `navigation.current` instead of the entire shape of a reference
const navigationRefVal = useNavigationContainerRef();
// We need here the entire shape, so we re-create it and pass it down into the `ref` prop for the `SophonzNavigationTracker` component.
const navigationRef = useRef(navigationRefVal);
if (isLoadingTracerProvider || tracerProvider === null) {
return (
<View>
<View>
<Text>Loading Tracer Provider...</Text>
</View>
</View>
);
}
return (
// `NavigationContainer` is waiting for what `useNavigationContainerRef` is returning (both exported from `@react-navigation/native`)
<NavigationContainer ref={navigationRefVal}>
<SophonzNavigationTracker
ref={navigationRef}
tracerProvider={tracerProvider}
screenAttributes={{
"static.attribute": 123456,
"custom.key": "abcd...",
}}>
<Tab.Navigator
screenOptions={{
tabBarLabelPosition: "beside-icon",
tabBarIconStyle: {display: "none"},
}}>
<Tab.Screen
name="cart"
options={{
tabBarAccessibilityLabel: "Cart",
}}
component={CartPage}
/>
<Tab.Screen
name="checkout"
options={{
tabBarAccessibilityLabel: "Checkout",
}}
component={CheckoutPage}
/>
... rest of tabs
</Tab.Navigator>
</SophonzNavigationTracker>
</NavigationContainer>
);
};
export default App;NOTE: If you are using @react-navigation/native you need to wrap your entire application with the NavigationTracker component as described in their official documentation.
If you are using react-native-navigation you are also able to track navigation changes.
You have to make sure you wrap your entry view with the <SophonzNativeNavigationTracker /> component and initialize Sophonz as soon as possible to avoid missing telemetry data.
// index.ts
import React, {useRef} from "react";
import {SophonzNativeTracerProvider} from "@sophonz/react-native-tracer-provider";
import {TracerProvider} from "@opentelemetry/api";
import {SophonzNativeNavigationTracker} from "@sophonz/react-native-navigation";
import {initialize} from "@sophonz/react-native";
import {Navigation} from "react-native-navigation";
import {HomeScreen} from "screens/HomeScreen";
const initApp = async () => {
// this example is showing how we can initialize Sophonz not using hooks
await initialize({
sdkConfig: {
ios: {collectorUrl: "https://in.sophonz.ai", appKey: "sk_..."},
},
});
let sophonzTracerProvider: TracerProvider;
try {
sophonzTracerProvider = new SophonzNativeTracerProvider();
} catch (e) {
console.log(
"Error creating `SophonzNativeTracerProvider`. Will use global tracer provider instead",
e,
);
}
Navigation.registerComponent(
"HomeScreen",
() =>
(props) => {
// make sure to wrap the events registry instance in a React ref
const navRef = useRef(Navigation.events());
return (
<SophonzNativeNavigationTracker
ref={navRef}
tracerProvider={sophonzTracerProvider}
screenAttributes={{
"test.attr": 98765,
dev: true,
}}>
<HomeScreen {...props} />
</SophonzNativeNavigationTracker>
);
},
() => HomeScreen,
);
// ... rest of registration and configuration
};
// root of the app
initApp();Disable Auto Tracking for Native Screens
Sophonz also collects automatically the Native screens, if you do not want to see Native components in the Session you can disable it:
JavaScript
const App = () => {
const {isPending, isStarted} = useSophonz({
ios: {
collectorUrl: "https://in.sophonz.ai",
appKey: "sk_...",
disableAutomaticViewCapture: true, // disabling the feature just for iOS
},
});
if (isPending) {
return (
<View>
<Text>Loading Sophonz</Text>
</View>
);
} else {
if (!isStarted) {
console.log('An error occurred during Sophonz initialization');
}
}
// regular content of the application
return (
...
);
}
export default AppAndroid
In android/app/src/main/sophonz-config.json:
{
"sdk_config": {
"app_framework": "react_native",
"ingest": {"service_key": "sk_..."},
"view_config": {
"enable_automatic_activity_capture": false
}
}
}iOS
When the SDK is started natively, remove the view capture service in SophonzInitializer.swift:
import Foundation
import SophonzIO
@objcMembers class SophonzInitializer: NSObject {
static func start() -> Void {
do {
let services = CaptureServicesOptionsBuilder()
.addDefaults()
.remove(ofType: ViewCaptureService.self)
.build()
try Sophonz.start(
options: .withCollector(
url: "https://in.sophonz.ai",
appKey: "sk_...",
platform: .reactNative,
captureServices: services
)
)
} catch let e {
print("Error starting Sophonz \(e.localizedDescription)")
}
}
}