# 让 App 检查更新

添加根组件，显示更新状态，设置何时下载和生效。

## 接入根组件 {#basic}

完成[原生接入](/docs/getting-started)后，用 Provider 包裹根组件。替换为对应平台的 appKey：

```tsx title="Root.tsx"
import { Pakta, UpdateProvider } from 'rn-update';
import App from './App';

const client = new Pakta({
  appKey: 'YOUR_PLATFORM_APP_KEY',
  updateStrategy: 'silentAndLater',
});

export default function Root() {
  return (
    <UpdateProvider client={client}>
      <App />
    </UpdateProvider>
  );
}
```

普通 RN 工程把注册入口指向这个 Root，保留你已有的应用名称：

```tsx title="index.js"
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import Root from './Root';

AppRegistry.registerComponent(appName, () => Root);
```

Expo Router 工程在 `app/_layout.tsx` 中让 Provider 包裹现有的 `Stack` 或 `Slot`。同一进程只挂载一个 Provider，客户端在组件外创建，避免重渲染时重复初始化。

### 多平台 appKey {#platform-key}

只有同时发布多个平台时才需要按平台选择。CLI 的 `selectApp` 会在 `update.json` 写入对应平台的 `appId` 和 `appKey`；先分别选好应用，再用下列代码替换单平台的 appKey：

```tsx title="按平台读取配置"
import { Platform } from 'react-native';
import updateConfig from './update.json';

const platform = Platform.OS;
const configs = updateConfig as Record<string, { appKey: string }>;
const appKey = configs[platform]?.appKey;
if (!appKey) throw new Error(`Missing Pakta appKey for ${platform}`);
```

导入 JSON 后，其中数据会进入 App bundle。文件中只保存应用标识，不放 `PAKTA_API_TOKEN`。

## 显示版本 {#use-update}

把下面组件放进 Provider 内部的页面。`currentHash` 为空表示运行内置 bundle。

```tsx title="UpdateStatus.tsx"
import { Button, Text, View } from 'react-native';
import { useUpdate } from 'rn-update';

export function UpdateStatus() {
  const { packageVersion, currentHash, lastError, checkUpdate } = useUpdate();
  return (
    <View>
      <Text>Pakta demo A</Text>
      <Text>Native: {packageVersion}</Text>
      <Text>Update: {currentHash || 'embedded'}</Text>
      <Button title="Check update" onPress={() => { void checkUpdate(); }} />
      {lastError ? <Text>{lastError.message}</Text> : null}
    </View>
  );
}
```

## 选择生效时机 {#update-strategy}

| 配置 | 用户体验 | 适合什么场景 |
| --- | --- | --- |
| `silentAndLater` | 静默下载，后续启动生效 | 本教程与不中断操作的更新 |
| `silentAndNow` | 下载完成立即重载 | 可以接受立即中断的场景 |
| `alertUpdateAndIgnoreError` | 提示用户更新，忽略检查错误 | SDK 默认策略 |
| `alwaysAlert` | 更新和错误都提示 | 内部诊断 |

`checkStrategy` 默认 `both`（启动和回到前台）；也可选 `onAppStart`、`onAppResume`。设为 `null` 关闭 JS 自动检查，原生冷启动检查仍可能下载更新；它不等于关闭全部更新能力。

## 手动更新 {#manual}

自定义更新按钮时设置 `checkStrategy: null`，在 Provider 内取得以下方法：

```tsx
const { client } = useUpdate();

async function onUpdate() {
  if (!client) return;
  const info = await client.checkUpdate();
  if (!info?.update) return;
  const hash = await client.downloadUpdate(info);
  if (hash) await client.switchVersion(hash);
}
```

按钮执行期间禁用重复点击，并展示捕获的错误。`useUpdateProgress()` 返回下载进度，progress 存在时范围为 0–100。

## 健康确认与恢复 {#health}

Provider 默认等待 1000 ms 后自动确认健康。关键初始化较晚时，把下面选项加入现有客户端，而不是新建第二个实例：

```tsx title="延迟健康确认"
autoMarkSuccessDelayMs: 5000,
healthCheck: () => myCriticalModulesReady(),
```

`myCriticalModulesReady` 是你自己的就绪判断。返回 `false` 或抛错会跳过本次确认。需要完全接管时设置 `autoMarkSuccess: false`，在关键页面就绪后调用 `useUpdate().markSuccess()`。

未确认健康的更新启动失败时，原生保护可在后续启动回退；已经确认健康后的任意业务错误不保证触发回退。原生救援仍依赖网络、有效配置和可用修复投放。

## 扩展 {#hooks}

- 检查、下载与重载拦截：查 [ClientOptions](/docs/api#client-options)。
- 测试二维码：`useUpdate().parseTestQrCode(code)`；生产可设 `testChannel: false`。[测试通道](/docs/integration#test-channel)。
- 错误与更新版本信息关联：[错误诊断](/docs/errors)。

## 测试通道 {#test-channel}

测试二维码用于指定测试更新，与原生包的固定分发渠道不是同一概念。首次接入不依赖扫码，按默认渠道发布即可。只有需要测试码入口时才在业务 UI 中接入解析，并控制哪些构建允许使用。

## 下一步 {#next}

保留手机上这份 Release 包，进入[发布第一条更新](/docs/publish)，把 `Pakta demo A` 改为 `Pakta demo B`。

[完整按钮、错误处理和进度示例](/docs/api#custom-update)。
