Mobile Development 13 min read

Implementing Visual Event Tracking in React Native Applications

The article details a visual‑event‑tracking solution for React Native that uses a custom SDK exposing higher‑order touchable components, a WebSocket link to a tracking platform, FiberNode inspection for props/state extraction, a Babel plugin for display names, and idle‑time queuing, allowing non‑code instrumentation and rapid tracking deployment.

Shopee Tech Team
Shopee Tech Team
Shopee Tech Team
Implementing Visual Event Tracking in React Native Applications

The article describes a visual event‑tracking (埋点) solution built for a React Native application used by Shopee’s local‑life front‑end team.

Background: The team needed to collect user behavior data without repeatedly adding code‑based instrumentation and redeploying. They evaluated “visual tracking” and “full (no) tracking”, discarding the latter because it generates massive useless data.

In web, visual tracking typically listens to document click events via event delegation, embeds the target page in an iframe , and communicates with the platform through postMessage . This approach cannot be used in React Native because iframe and postMessage are unavailable.

System Overview: The solution consists of a React Native SDK, a visual‑tracking platform, and a WebSocket channel that links the client and platform. The platform creates a sessionId and registers both the web‑side and React Native WebSocket clients.

Client SDK Integration

Developers import the SDK and call initGoblin to obtain a TouchableComponent object. The object exports higher‑order components such as GButton , GTouchableHighlight , etc., which accept a trackId prop.

import { initGoblin } from '@dp/goblin-sdk-react-native';
export const { TouchableComponent } = initGoblin({ ... });
const {
  GButton,
  GTouchableHighlight,
  GTouchableNativeFeedback,
  GTouchableOpacity,
  GTouchableWithoutFeedback
} = TouchableComponent;
Click Me

These components wrap the original React Native components and inject tracking logic.

Connecting Client and Platform

After SDK integration, the client establishes a WebSocket connection to the visual‑tracking platform. The platform generates a sessionId and sends it to the front‑end:

{ "25089": { "creator": adminWSClient } }

The React Native client enters the configuration page, inputs the sessionId , and connects via WebSocket. The server registers both ends:

{ "25089": { "creator": adminWSClient, "connector": rnWSClient } }

Once the connection is successful, the platform notifies both sides, and the SDK starts sending element‑selection data to the platform.

Tracking Configuration

When a user selects an element in the React Native client, the SDK highlights the element and sends its trackId together with a collection of data sources derived from the component’s props and state . The platform allows configuration of additional fields, e.g., adding a title field whose value comes from Item.props.title .

The unique identifier trackId can be a manually assigned ID or an automatically generated XPath‑like path obtained by traversing the component’s FiberNode tree via this._reactInternals . Because the generated IDs differed between Android and iOS, the team settled on manually assigning trackId .

During traversal, the SDK extracts memoizedProps and memoizedState from each ancestor FiberNode , which correspond to the component’s props and state . This yields the data‑source collection needed for reporting.

Handling Obfuscated Component Names

In production builds, component names are minified, making manual configuration difficult. The team created a Babel plugin (based on babel-plugin-add-react-displayname ) that injects a displayName property into each component, allowing the SDK to use displayName instead of the obfuscated name.

Event Reporting

When the page loads, the SDK fetches the latest JSON configuration file. To avoid losing user actions that occur before the fetch completes, a queue stores all events. The queue is processed inside requestIdleCallback , ensuring it runs during idle periods.

Once the configuration is available, the SDK matches each queued event’s trackId with the configuration. If a match is found, it gathers the variable values from the component’s FiberNode (e.g., props.Item.title ) and merges them with constant fields (e.g., operation: "click" ) before sending the final payload.

{
  "item-button": {
    "constant": { "operation": "click" },
    "variable": { "title": "props.Item.title" }
  }
}

The resulting report looks like:

{
  "operation": "click",
  "title": "Second Item"
}

Conclusion

The solution combines several advanced techniques: higher‑order components, React internal FiberNode inspection, WebSocket communication, Babel code transformation, and idle‑time processing. It enables non‑code‑changing visual tracking for React Native apps, reduces repetitive instrumentation work, and improves the speed of launching new tracking requirements.

SDKevent analyticsvisual trackingReact NativeHigher-Order Components
Shopee Tech Team
Written by

Shopee Tech Team

How to innovate and solve technical challenges in diverse, complex overseas scenarios? The Shopee Tech Team will explore cutting‑edge technology concepts and applications with you.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.