Skip to content
AI 文档索引 ↗

React Native SDK ​

AI 辅助接入 React Native

从 React Native SDK 下载页获取离线集成包,并使用包内配套的 Android/iOS 原生组件。适用版本以下载页为准。


一、 接入前确认 ​

  • 向项目负责人获取测试环境的 endpoint 和公开 projectKey;在真机上使用设备可访问的地址。
  • SDK 使用包内配套的 Android/iOS 原生组件管理身份、队列和上传。
  • 以下示例适用于宿主已确认允许基础采集的情况。需要事前授权时,初始化使用 initialConsent: 'PENDING',由宿主在用户同意后调用 client.setConsent('GRANTED')。
  • Expo 项目需要 Prebuild 或 Development Build;Expo Go 不包含这些原生模块。
  • 事件属性和路由名称应避免包含凭据或不必要的个人信息。

二、 安装与原生依赖配置 ​

解压离线包后,按宿主项目类型选择以下安装方式。

1. Bare React Native 项目 ​

bash
# 从 React Native 下载页获取 ZIP,解压后将 react-native/ 目录复制到工程 libs/
pnpm add ./libs/react-native

iOS 依赖安装: ​

bash
cd ios
pod install

Android 依赖验证: ​

离线包的 react-native/android/maven/ 含 Track Business 核心 AAR、POM 和 Gradle Module。在宿主已有的 android/settings.gradle 的 dependencyResolutionManagement.repositories 中加入以下仓库(保留项目现有仓库):

groovy
// android/settings.gradle
dependencyResolutionManagement {
  repositories {
    exclusiveContent {
      forRepository { maven { url = uri(file('../node_modules/@track-business/sdk-react-native/android/maven')) } }
      filter { includeGroup('com.trackbusiness') }
    }
    google()
    mavenCentral()
  }
}

旧版宿主没有 dependencyResolutionManagement 时,可在项目级 android/build.gradle 使用:

查看旧版 Gradle 配置
groovy
allprojects {
  repositories {
    exclusiveContent {
      forRepository { maven { url = uri(rootProject.file('../node_modules/@track-business/sdk-react-native/android/maven')) } }
      filter { includeGroup('com.trackbusiness') }
    }
    google()
    mavenCentral()
  }
}

两种方式按宿主配置选择其一,不要把 allprojects 放到 settings.gradle。google() 和 mavenCentral() 用于 AndroidX、Kotlin 等通用依赖;Track Business 原生 SDK 只从本地 Maven 目录解析。启用 FAIL_ON_PROJECT_REPOS 或 PREFER_SETTINGS 的宿主使用 settings 仓库块,并保留项目已有的第三方仓库。

2. Expo 项目 ​

先按本节第 1 步下载并安装 React Native SDK。Expo 项目需要使用 Prebuild 或 Development Build,不能通过 Expo Go 加载 SDK。

在项目的 app.json 或 app.config.js 中添加插件配置:

json
{
  "expo": {
    "plugins": ["@track-business/sdk-react-native"]
  }
}

在项目根目录运行以下命令,生成 Android 和 iOS 工程:

bash
npx expo prebuild

**Android:**预构建后,按上面的「Android 依赖验证」配置本地 Maven 仓库。SDK 插件不会自动完成此配置。然后在项目根目录运行:

bash
npx expo run:android

**iOS:**安装 CocoaPods 依赖,然后在项目根目录运行应用:

bash
(cd ios && pod install)
npx expo run:ios

使用 npx expo prebuild --clean 会重新生成原生工程,可能覆盖对原生工程的手动修改。重新生成后,请再次检查并配置 Android 本地 Maven 仓库。如使用自定义 Expo 插件管理该配置,也请确认生成的工程中包含该仓库。

3. 原生依赖说明 ​

React Native 离线包包含接入所需的 Android 和 iOS 原生 SDK:

  • Android:Gradle 从 SDK 目录内的本地 Maven 仓库读取原生依赖。如果文件缺失,构建会失败,请重新检查下载包及仓库路径。
  • iOS:CocoaPods 使用 SDK 包内的原生框架。如果框架文件缺失,依赖安装会失败,请重新检查下载包。

三、 快速接入与 Provider 挂载 ​

1. 根组件初始化与挂载 ​

tsx
import { useEffect, useState } from 'react';
import {
  TrackBusinessRNClient,
  TrackBusinessProvider,
  TrackBusinessSDKError,
  useTrackBusiness,
  useTrackEvent,
} from '@track-business/sdk-react-native';
import { View, Text, Button } from 'react-native';

export default function App() {
  const [client, setClient] = useState<TrackBusinessRNClient | null>(null);

  useEffect(() => {
    let mounted = true;
    void TrackBusinessRNClient.init({
      endpoint: 'https://collector.example.com/v1/events/batch',
      projectKey: 'your-public-project-key',
      initialConsent: 'GRANTED',
    })
      .then((instance) => {
        if (mounted) setClient(instance);
      })
      .catch((error: unknown) => {
        if (!mounted) return;
        const code = error instanceof TrackBusinessSDKError ? error.code : 'TB_INTERNAL_ERROR';
        console.warn('Track Business 初始化未完成', { code });
      });

    return () => {
      mounted = false;
    };
  }, []);

  return (
    <TrackBusinessProvider client={client}>
      <HomeScreen />
    </TrackBusinessProvider>
  );
}

SDK 初始化失败时仍渲染业务页面,Provider 接受 null,useTrackEvent() 在此状态下不执行采集。直接调用 Client 的异步 API 时,应处理失败,避免影响登录、退出和其他业务流程。SDK 实例由应用根管理,不在业务页面反复初始化。

原生端在后台完成存储和身份恢复,init() 完成后才提供可用实例。根组件可以先渲染,等实例就绪后再交给 Provider。

2. 记录首个事件 ​

tsx
function HomeScreen() {
  const client = useTrackBusiness();
  const track = useTrackEvent();
  const handleTestEvent = async () => {
    await track('sdk_integration_check', { source: 'react-native-docs' });
  };

  return (
    <View>
      <Text>Track Business React Native</Text>

      <Button title="发送测试事件" disabled={!client} onPress={() => void handleTestEvent()} />
    </View>
  );
}

3. 验证首条事件 ​

初始化完成且授权允许采集后,点击“发送测试事件”,检查原生端的队列与诊断状态,再通过目标测试 Collector 核对事件与有效整批 ACK。useTrackEvent 会隔离采集错误,调用完成不等于接收成功。检查方法与环境范围见数据校验。

按需关联用户身份 ​

业务认证成功后,通过已初始化的 Client 调用 identify;安全退出时调用 signOut。这些异步调用可能失败,宿主应处理错误,不能因此中断登录或退出。

tsx
function useAnalyticsIdentity() {
  const client = useTrackBusiness();

  async function identifyAuthenticatedUser(userId: string) {
    if (!client) return;
    try {
      await client.identify(userId);
    } catch {
      // 记录脱敏的 SDK 状态,业务登录仍按宿主流程继续。
    }
  }

  async function signOutAnalytics() {
    if (!client) return;
    try {
      await client.signOut();
    } catch {
      // 业务退出仍按宿主流程继续。
    }
  }

  return { identifyAuthenticatedUser, signOutAnalytics };
}

切换账号、匿名连续性及错误处理见身份与生命周期。


四、 可选:记录 React Navigation 页面变化 ​

需要记录 React Navigation 的页面切换时,将监听器接入 NavigationContainer。监听器仅处理已接入的导航变化,不会自动采集整个应用;同名路由的参数变化不会触发新事件。请使用稳定的路由名称,避免包含订单号等动态值。路由名称规整不等同于个人信息脱敏:

tsx
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { useMemo } from 'react';
import { createNavigationStateChangeListener } from '@track-business/sdk-react-native/navigation';

export function NavigationRoot() {
  const navigationRef = useNavigationContainerRef();
  const client = useTrackBusiness();

  const onNavigationStateChange = useMemo(() => {
    if (!client) return undefined;
    return createNavigationStateChangeListener(client, navigationRef);
  }, [client, navigationRef]);

  return (
    <NavigationContainer ref={navigationRef} onStateChange={onNavigationStateChange}>
      {/* Stack.Navigator & Screens */}
    </NavigationContainer>
  );
}

五、 状态与诊断方法 ​

ts
// 强制触发原生 Outbox 发送
const result = await client.flush();
// success 表示原生 flush 调用完成,不等于 Collector ACK。
console.log(`Flush invoked: ${result.success}`);

// 获取原生健康快照
const health = await client.getHealthSnapshot();
console.log(`Outbox queue size: ${health.queueSize}`);

// 动态设置数据采集 Opt-Out
await client.setOptOut(true);

兼容性与授权 ​

Android 宿主提供 AGP/Kotlin 插件:Java 17、Kotlin 2.0.21 或更新兼容版本、compileSdk 35+、minSdk 23+。请核对 React Native、原生依赖与宿主工具链的兼容性。

iOS 使用包内配套的原生模块。事前授权使用 initialConsent: 'PENDING' 或 deferredCapture: true;用户同意后调用 setConsent('GRANTED')。PENDING 只用于初始化,不能通过运行时 setConsent 设置。授权失败时 Promise 会 reject;请处理错误后再记录依赖授权的事件。

导入包不会自动初始化或采集;请在应用启动流程中创建并复用客户端。

属性校验失败会返回 TB_INVALID_PROPERTIES,本次调用不会记录事件或更新属性。请精简过长的字段值。

公共属性 ​

公共属性委托给原生运行时,只影响之后创建的事件并保存在内存中;应用重新初始化后需重新设置。账号边界变化时,移除不应继续共享的自定义属性。

ts
await client.setGlobalProperties({ appEdition: 'team' });
await client.removeGlobalProperties(['appEdition']);

位置快照 ​

ts
// 宿主已取得系统定位权限和业务定位授权后显式启用。
await client.grantLocationConsent();
await client.trackWithLocation('store_visit', {
  latitude: 31.2,
  longitude: 121.5,
  coordinateSystem: 'WGS84',
  observedAt: Date.now(),
});
await client.revokeLocationConsent();

SDK 不申请系统定位权限或主动获取位置;宿主提供已授权的坐标。授权、撤销、有效期和已发送数据的处理见事件级位置快照。

出现 TB_NATIVE_UNAVAILABLE 时,请核对 RN 包与原生依赖是否配套安装。

用户画像 ​

先绑定测试用户,再按需写入画像属性:

ts
await client.identify('customer-42');
await client.setUserProperties({ tier: 'pro', score: 2 });
await client.setUserPropertyOnce({ source: 'app' });
await client.profileIncrement({ score: 1 });
await client.profileAppend({ tags: ['campaign-a'] });
await client.unsetUserProperties(['source']);
const localProfile = await client.getUserProperties();
await client.clearUserProperties();

getUserProperties() 返回原生本地缓存;clearUserProperties() 只提交本地已知属性的移除,不删除服务端全部画像。profileIncrement 接受有限数字,profileAppend 接受同类型的字符串或数字数组。

Android 局域网 HTTP 排查 ​

RN 宿主出现 NoRouteToHostException 或 HTTP 请求失败时,检查设备能否访问接收地址,以及当前 Wi-Fi 的代理配置。ping 成功不能证明应用请求可用。

测试时可按宿主网络策略修正失效代理并复验;不要在 SDK 中强制绕过系统代理。局域网 HTTP 还需检查宿主网络安全配置。

接入时确认投递 ​

若所用 RN 包和配套原生包均提供 verifyDelivery,可在测试中观察当前待发范围。先从应用已初始化的实例获取 Client:

typescript
const client = TrackBusinessRNClient.getInstance();
if (client) {
  await client.track('integration_check');
  const report = await client.verifyDelivery(3000);
  // 根据 report.status 和 report.reason 判断确认结果。
}

根据 report.status 和 report.reason 判断结果。flush().success 仅表示调用完成;结果含义见可靠投递。