DocumentaçãoEnviar dados
SDK React Native
Instrumente apps iOS e Android com @holdyapp/react-native: track, identify, group e screen, com sessão pelo ciclo do app, fila que sobrevive a reinício, erros de JS e as mudanças da Holdy na tela.
O SDK React Native (@holdyapp/react-native) instrumenta apps iOS e Android. Usa o mesmo núcleo do SDK web: mesma fila, mesmo envio em lote pra POST /api/v1/events/bulk, mesmo retry e o mesmo sorteio das mudanças. Nenhum método público lança exceção.
Instalação
npm install @holdyapp/react-native @react-native-async-storage/async-storage
cd ios && pod install
O @react-native-async-storage/async-storage guarda o id anônimo, o usuário, a conta, a sessão e a fila entre aberturas do app. Sem ele o SDK funciona em memória e perde o que estava na fila quando o app fecha. No Expo, npx expo install @react-native-async-storage/async-storage.
Setup
Use uma chave sem restrição de origem: app nativo não manda Origin, e chave restrita a domínios recusa os eventos.
import { HoldyProvider } from '@holdyapp/react-native'
export default function App() {
return (
<HoldyProvider options={{ apiKey: 'hol_live_...', release: '2.3.0+41' }}>
<Routes />
</HoldyProvider>
)
}
Fora do React, initHoldy({ apiKey }) devolve a mesma instância, e useHoldy() a entrega em qualquer componente.
Opções: apiUrl (padrão https://holdy.chat/api/v1), release (versão do app no ar; com ela a Holdy diz em que versão um erro apareceu), appVersion e appBuild, deviceInfo, captureErrors (padrão ligado), productChanges (padrão ligado), screenPath, flushAt, flushIntervalMs e debug.
Versão e build do app entram sozinhos se você passar o react-native-device-info, que é opcional:
import DeviceInfo from 'react-native-device-info'
initHoldy({ apiKey: 'hol_live_...', deviceInfo: DeviceInfo })
Usuário e conta
const holdy = useHoldy()
holdy.identify(user.id, { role: 'admin' })
holdy.group(company.id, { plan: 'scale', mrr: 1490, status: 'paying' })
holdy.track('invite_sent', { plan: 'pro' })
Se quem paga é a própria pessoa, sem empresa por trás, chame group(user.id): a conta é a pessoa, e é ela que entra nas oportunidades e na leitura das ações. Os traits de group são os mesmos do SDK web (plan, mrr, status, industry, employees). Usuário e conta ficam guardados no aparelho; no logout, chame holdy.reset().
Telas
holdy.screen('Checkout') é a visualização de página do app. Sai como $pageview com screen_name, e todo evento seguinte carrega path: '/screen/Checkout' no contexto. É por esse caminho que telas viram comportamento, entram nos caminhos observados e no Flow, e é nele que uma mudança da Holdy é posta no ar. Pra usar outro caminho (por exemplo o do deep link), passe screenPath: (name) => ....
Com React Navigation, uma linha registra toda troca de tela:
import { NavigationContainer, createNavigationContainerRef } from '@react-navigation/native'
import { useHoldyNavigationTracking } from '@holdyapp/react-native'
const navigationRef = createNavigationContainerRef()
function Routes() {
useHoldyNavigationTracking(navigationRef)
return <NavigationContainer ref={navigationRef}>{/* ... */}</NavigationContainer>
}
O nome da tela é o name da rota ativa, inclusive em navegador aninhado. Quem prefere os props do container usa holdyNavigationHandlers(holdy, navigationRef), que devolve onReady e onStateChange.
Mudanças no produto
Quando uma dica ou troca de texto vai pro ar na Holdy pra uma tela do app, o SDK sorteia quem vê (o mesmo sorteio do web, por conta, pessoa ou visitante conforme a mudança) e registra a exposição dos dois grupos, pra Holdy comparar com quem ficou de fora.
import { HoldyTip, useHoldyChange } from '@holdyapp/react-native'
<HoldyTip anchor="connect-source" style={{ marginTop: 12 }} />
function SaveButton() {
const change = useHoldyChange('save-button', { currentText: 'Salvar' })
return <Button title={change?.text ?? 'Salvar'} testID="save-button" />
}
A âncora casa com o testID do elemento ([data-testid="save-button"] na mudança), com o texto dele, ou com "page" pra mudança da tela inteira. O HoldyTip tem estilo neutro; style, textStyle e render deixam ele com a cara do app. Fechar a dica emite $change_dismissed e ela não volta. Pergunta e checklist ainda são só do web.
Como o SDK se comporta
- Sessão — uma sessão dura enquanto o app está em uso. Depois de 30 minutos em background, ou 30 minutos sem evento, a próxima abertura começa outra. O id vai em
context.session_id. - Envio — fila com flush a cada 20 eventos ou 5 segundos e quando o app vai pro background. A fila fica guardada no aparelho: o que não saiu por falta de rede sai na próxima abertura, e o id de cada evento impede duplicar.
- Contexto — todo evento leva
platform(iosouandroid),os,os_version,device(mobileoutablet), tamanho da tela, idioma, versão e build do app quando informados, elibrarycom nome e versão do SDK. - Erros de JS — o handler global do React Native vira
$js_error, com mensagem, até 8 linhas da pilha efatalquando o app ia fechar. O handler que já existia continua sendo chamado.
Problemas comuns
- Nenhum evento chega — ligue
debug: true. Um 403 quase sempre é chave restrita a origens: crie uma chave sem restrição pro app. - Eventos somem quando o app fecha — falta o
@react-native-async-storage/async-storage, ou opod installdepois de instalar. - A dica não aparece — confira se a conta foi identificada com
group(), se a tela tem o nome do molde da mudança (/screen/<Nome>) e se a âncora bate com otestID. Parte das contas fica de fora por sorteio, e pra elas a dica nunca aparece. - Telas com nome errado — o SDK usa o
nameda rota. Se as rotas se chamamScreen1, renomeie ou passescreenPath.