---
title: "Procesar datos de flows en React Native SDK"
description: "Guarda y usa los datos que tus usuarios introducen en flows en tu app de React Native con el SDK de Adapty."
---

> **AI agents**: to search Adapty docs faster and with fewer tokens, install the Adapty skill. Claude Code (self-updating via plugin): `claude plugin marketplace add adaptyteam/adapty-skills && claude plugin install adapty-skills@adapty` — other tools: `npx skills add adaptyteam/adapty-skills --all`

Cuando un usuario escribe en un campo de texto, responde un cuestionario o activa un interruptor en un [flow](adapty-flow-builder), el SDK pasa el valor a tu app a través de su callback de análisis.

Las apps suelen usar esos datos para:

- **Registrar usuarios en tu propio backend**: Toma el correo electrónico y el nombre que el usuario introdujo en tu onboarding, y crea su cuenta cuando el flow se cierre.
- **Guardar respuestas y preferencias**: Registra lo que eligió el usuario para que tu app pueda usarlo más adelante — por ejemplo, [escríbelo en su perfil de Adapty](react-native-setting-user-attributes) como atributos personalizados.
- **Personalizar flows futuros**: Guarda las respuestas del cuestionario como atributos personalizados y luego apunta a un placement posterior para que cada segmento vea un flow distinto, o un paywall diferente dentro de él.
- **Enviar datos a plataformas de analítica de terceros**: Reenvía las respuestas a Amplitude, Mixpanel o cualquier plataforma de analítica de producto que utilices.

Las entradas y los grupos seleccionables reportan sus valores automáticamente. Para distinguirlos en tu código, asigna a cada entrada un **Element ID** significativo y a cada grupo seleccionable un **Group ID** en el builder.

## Antes de empezar

Necesitas:

- **Adapty SDK v4 o posterior**: Los callbacks de flow no existen en versiones anteriores.
- **Un flow creado en el [Flow & Paywall Builder](adapty-flow-builder)**: Solo los flows reportan valores de entrada a través de este callback.
- **Una versión de flow publicada recientemente**: Un flow solo reporta valores de entrada si lo publicaste después de que esta función estuviera disponible. Si no llega nada a tu app, publica una nueva versión del flow e inténtalo de nuevo.

## Recibir valores de entrada \{#receive-input-values\}

Los valores de entrada llegan al mismo handler que cualquier otro evento de analítica de un flow, bajo el nombre de evento `flow_user_input`. Registra `onAnalytics` junto con el resto de handlers de eventos del flow:

```javascript showLineNumbers title="React Native"
const unsubscribe = view.setEventHandlers({
  onAnalytics(name, params) {
    handleFlowInput(name, params);
    return false; // keep the flow open
  },
});
```

El callback `onAnalytics` entrega todos los eventos de analítica de un flow, incluidas las [vistas de pantalla](react-native-flow-screen-views).
- El parámetro `name` contiene el nombre del evento. Para filtrar los eventos de entrada del usuario, compara `name` con `flow_user_input`.
- El parámetro `element_type` indica la categoría del elemento.
- El valor de la entrada se almacena en distintos parámetros según el tipo de elemento:
     - Los campos de texto, selectores y toggles guardan la entrada del usuario en `value`
     - Los grupos seleccionables reportan las opciones activas en `item_ids` e `item_titles`

```javascript showLineNumbers title="React Native"
function handleFlowInput(name, params) {
  if (name !== 'flow_user_input') return;

  // The screen the input sits on. Pair it with element_id to tell apart
  // two fields that share an Element ID on different screens.
  const screenId = params.instanceId;

  switch (params.element_type) {
    case 'text_input':
    case 'email_input':
    case 'number_input':
    case 'phone_input': {
      const text = params.value;
      break;
    }
    case 'date_picker':
    case 'time_picker':
    case 'date_time_picker': {
      // Unix time in milliseconds.
      const date = new Date(params.value);
      break;
    }
    case 'single_choice': {
      const optionId = params.item_ids[0];
      break;
    }
    case 'multi_choice': {
      const optionIds = params.item_ids;
      break;
    }
    case 'toggle': {
      const isOn = params.value;
      break;
    }
  }
}
```

Para confirmar que el callback se ejecuta, interactúa con el input en una versión de prueba de tu propia app. Si tu callback handler no recibe ningún evento, revisa los [prerequisitos](#before-you-start). Asegúrate de que el flow fue publicado después de que esta función estuviera disponible.

## Cuando tu app recibe el input \{#when-your-app-receives-the-input\}

:::important
El valor predeterminado de entrada o selección nunca llega a tu aplicación a través de este callback. Si un usuario acepta la opción marcada como **Set as default** y continúa, no se dispara ningún evento. No interpretes la ausencia del evento como "sin respuesta": el usuario simplemente dejó el valor predeterminado tal como estaba.
:::

**Los siguientes elementos disparan este evento:**

- Campos de texto, email, número y teléfono
- Selectores de fecha, hora y fecha-hora
- Grupos seleccionables de opción única y múltiple, y toggles

**Los siguientes no lo hacen:**

- Campos de contraseña, selecciones de productos y cambios de pestaña
- Inputs dentro de un elemento Header, que se comparte entre pantallas
- Grupos seleccionables con IDs de opción duplicados o faltantes, o con un ID de grupo reutilizado en otra pantalla. Ese tipo de grupo no envía nada en absoluto, en lugar de enviar parte de la respuesta.

**El evento se dispara cuando:**

- Un campo pierde el foco. Un campo vaciado reporta una cadena vacía; un campo que el usuario nunca editó no reporta nada. Un marcador de posición no es un valor. Si el usuario vuelve al campo, lo edita y lo abandona de nuevo, se genera un segundo evento.
- El usuario cierra un selector tras elegir un nuevo valor. Cerrarlo sin cambios no reporta nada.
- El usuario pulsa una opción o un toggle. Un evento de opción múltiple lista todas las opciones seleccionadas, por lo que deseleccionar la última envía dos arrays vacíos.

**El evento no se dispara cuando:**

- El usuario escribe. No hay flujo de pulsaciones de teclas, solo el valor que tiene el campo cuando pierde el foco.
- El usuario envía o cierra el flow. Un valor que aún se está editando en ese momento puede perderse; la sección de [limitaciones de entrega](#delivery-and-limitations) explica cómo diseñar la última pantalla teniendo esto en cuenta.
- Se establece un valor sin interacción del usuario. Una opción marcada como **Set as default** se preselecciona al abrirse la pantalla, y una acción **Set Variable** puede seleccionar una opción o rellenar un input a partir de otra interacción. Ninguna de las dos envía un evento; un input prerrellenado solo se reporta una vez que el usuario lo edita.

## Qué recibes \{#what-you-receive\}

El callback entrega dos eventos. Filtra por `name` para mostrar únicamente los eventos `flow_user_input`. El payload JSON de respuesta tiene este aspecto:

```json
{
  "name": "flow_user_input",
  "instanceId": "scr_registration",
  "isBackendEvent": false,
  "isCustomerEvent": true,
  "element_id": "email",
  "element_type": "email_input",
  "value": "jane@example.com"
}
```

| Parámetro         | Descripción |
|:------------------|:------------|
| `name`            | `flow_user_input` para eventos de entrada, `flow_screen_showed` para vistas de pantalla. |
| `instanceId`      | El ID de la pantalla del input. Los IDs de elemento son únicos dentro de una pantalla, no en todo el flow. Si tu flow tiene inputs en más de una pantalla, combina `instanceId` con `element_id` al filtrar eventos. |
| `element_id`      | El **Element ID** del [input](builder-inputs-and-forms), o el **Group ID** del [grupo seleccionable](flow-selectable-elements). |
| `element_type`    | El tipo de elemento que envió el evento. Determina cuál de los parámetros siguientes contiene el valor del input. |
| `value`           | **Solo campos de texto, selectores y toggles.** El valor del input: una cadena para campos de texto, un entero para selectores, un booleano para toggles. |
| `item_ids`        | **Solo grupos seleccionables de elección única y múltiple.** Los **Element IDs** de las opciones seleccionadas, en el orden en que aparecen en el builder. Una entrada para un grupo de elección única; cualquier número para un grupo de elección múltiple. |
| `item_titles`     | **Solo grupos seleccionables de elección única y múltiple.** Los títulos de las opciones listadas en `item_ids`, en el mismo orden. Nunca está vacío: una opción sin título informa su ID en su lugar. |
| `isCustomerEvent` | Indicador de utilidad, siempre `true` para este evento. Marca los eventos que el SDK entrega a tu callback. Resulta útil si un mismo handler reenvía todos los eventos del flow a tu sistema de análisis y filtras por este indicador en lugar de por `name`. |
| `isBackendEvent`  | Indicador de utilidad, siempre `false` para este evento. Marca los eventos que Adapty también registra para su propio análisis. `false` confirma que lo que los usuarios introducen llega a tu app y solo a ella — Adapty no lo recibe ni lo almacena. |

Lo que reporta cada elemento:

| En el builder | `element_type` | Parámetro que almacena el valor | Qué contiene |
|:--------------|:---------------|:--------------------------------|:-------------|
| Input de **Texto**, **Número**, **Número de teléfono** | `text_input`, `number_input`, `phone_input` | `value` | La cadena literal que escribió el usuario. Los números llegan como cadenas, no como tipos numéricos. |
| Input de **E-mail** | `email_input` | `value` | La cadena literal que escribió el usuario, aunque no haya superado la validación de formato del builder. Valídala en tu lado antes de usarla. |
| Input de **Contraseña** | ninguno | ninguno | No envía ningún evento. |
| Input de **Fecha** | `date_picker` | `value` | Tiempo Unix en milisegundos, como entero, a medianoche local de la fecha seleccionada. |
| Input de **Hora** | `time_picker` | `value` | Tiempo Unix en milisegundos, como entero, redondeado hacia abajo al minuto. |
| Input de **Fecha y hora** | `date_picker` y `time_picker` | `value` | Dos elementos, un selector de fecha y un selector de hora. Cada uno envía su propio evento. |
| Input cambiado a **Fecha y hora** en el desplegable **Type** | `date_time_picker` | `value` | Tiempo Unix en milisegundos, como entero, redondeado hacia abajo al minuto. |
| Grupo de **elección única** | `single_choice` | `item_ids`, `item_titles` | Dos arrays. `item_ids`: un array con el Element ID de la opción seleccionada. `item_titles`: un array con el título de esa opción. |
| Grupo de **elección múltiple** | `multi_choice` | `item_ids`, `item_titles` | Dos arrays. `item_ids`: los Element IDs de todas las opciones seleccionadas, en el orden en que aparecen en el builder. `item_titles`: sus títulos, en el mismo orden. Ambos arrays están vacíos cuando no hay nada seleccionado. |
| Grupo de **Toggle** | `toggle` | `value` | Un booleano. |

Para bifurcar según una respuesta, compara `item_ids`, no `item_titles`. El título es derivado: el **Element Title** de la opción si lo definiste, si no su texto en tu idioma predeterminado, y si no su Element ID. Un usuario que vio el flow en otro idioma vio un texto diferente.

## Ejemplos de eventos \{#event-examples\}

Estos ejemplos muestran las propiedades disponibles en cada evento, con valores ilustrativos en los comentarios.

<Details>
<summary>Entrada de texto, correo electrónico, número y teléfono (Haz clic para expandir)</summary>

```javascript
onAnalytics(name, params) {
    name;                      // 'flow_user_input'
    params.name;               // 'flow_user_input'
    params.instanceId;         // 'scr_J260KU5q'
    params.isCustomerEvent;    // true
    params.isBackendEvent;     // false
    params.element_id;         // 'email'
    params.element_type;       // 'email_input'
    params.value;              // 'jane@example.com'   (string)
}
```
</Details>

<Details>
<summary>Selectores de fecha, hora y fecha-hora (Click to expand)</summary>

```javascript
onAnalytics(name, params) {
    params.element_id;         // 'birthday'
    params.element_type;       // 'date_picker'
    params.value;              // 645408000000   (Unix milliseconds — 1990-06-15, local midnight)
}
```
</Details>

<Details>
<summary>Single choice (Click to expand)</summary>

```javascript
onAnalytics(name, params) {
    params.element_id;         // 'experience'
    params.element_type;       // 'single_choice'
    params.item_ids;           // ['pro']
    params.item_titles;        // ['I train professionally']
}
```
</Details>

<Details>
<summary>Multi choice (Haz clic para expandir)</summary>

```javascript
onAnalytics(name, params) {
    params.element_id;         // 'interests'
    params.element_type;       // 'multi_choice'
    params.item_ids;           // ['sports', 'music']
    params.item_titles;        // ['Sports', 'Music']
}
```
</Details>

<Details>
<summary>Toggle (Haz clic para expandir)</summary>

```javascript
onAnalytics(name, params) {
    params.element_id;         // 'reminders'
    params.element_type;       // 'toggle'
    params.value;              // true   (boolean)
}
```
</Details>

## Entrega y limitaciones \{#delivery-and-limitations\}

:::warning
Los flows envían valores sin procesar: direcciones de correo electrónico, números de teléfono y cualquier otra cosa que el usuario escriba. Trata todo lo que entregue el callback de analíticas como datos personales y no los almacenes en ningún lugar donde no guardarías el correo electrónico de un usuario.
:::

- **El último valor prevalece**: recibes un evento por campo con el valor con el que el usuario se quedó, no una secuencia tecla a tecla. Si edita un campo, solo llega la última versión.
- **Sin garantía de envío**: los valores te llegan a medida que el usuario avanza por el flow, y puede salir en cualquier momento. Espera a que el flow se cierre antes de considerar un conjunto de respuestas como completo.
- **Entrega de mejor esfuerzo**: si un usuario cierra el flow o minimiza la app mientras un campo tiene el foco o un selector está abierto, ese valor puede perderse.

Para que el valor del último campo sea fiable, termina el flow con una pantalla que no tenga entradas ni selectores, y cierra el flow mediante una acción explícita del usuario en lugar de hacerlo automáticamente al aparecer esa pantalla. Pasar a la pantalla final quita el foco del campo anterior, que es lo que envía su valor.

Guarda cada valor de entrada tal como lo recibe tu handler y envía el conjunto completo cuando el flow se cierre. Para detectar ese momento, registra un handler `onDisappeared` junto a `onAnalytics`. Se ejecuta cuando la vista del flow se descarta, tanto si el usuario completó el flow como si lo cerró a mitad.

## Casos de uso \{#use-cases\}

### Registrar usuarios en tu backend \{#register-users-on-your-backend\}

Recopila los valores según van llegando y envíalos cuando el flow se cierre, para que una sola solicitud contenga el conjunto completo de respuestas.

El flow desaparece tanto si el usuario lo completó como si lo abandonó a mitad. Comprueba que tienes los campos necesarios antes de llamar a tu backend.

El flow no puede mostrar errores de tu backend. El valor de retorno del handler solo cierra la vista del flow, y el SDK no tiene ningún método que envíe datos a un flow en ejecución. Si el registro falla, por ejemplo porque el email ya está en uso, muestra el error en tu propia UI después de que el flow se cierre.

```javascript showLineNumbers title="React Native"
const flowAnswers = {};

const unsubscribe = view.setEventHandlers({
  onAnalytics(name, params) {
    if (name === 'flow_user_input' && typeof params.value === 'string') {
      flowAnswers[params.element_id] = params.value;
    }
    return false;
  },
  onDisappeared() {
    if (flowAnswers.email) {
      // Send flowAnswers to your backend here to create the account.
    }
    return false;
  },
});
```

### Enriquece los perfiles de usuario con datos \{#enrich-user-profiles-with-data\}

Para vincular lo que un usuario ha introducido a su perfil y evitar pedirle los mismos datos dos veces, [actualiza el perfil de usuario](react-native-setting-user-attributes) a medida que lleguen los valores.

Por ejemplo, si tu flow tiene un campo de texto con el ID de elemento `name` y un campo de email con el ID de elemento `email`:

```javascript showLineNumbers title="React Native"
function handleFlowInput(name, params) {
  if (name !== 'flow_user_input') return;
  if (typeof params.value !== 'string') return;

  const profileParams = {};

  switch (params.element_id) {
    case 'name':
      profileParams.firstName = params.value;
      break;
    case 'email':
      profileParams.email = params.value;
      break;
    default:
      return;
  }

  adapty.updateProfile(profileParams).catch(error => {
    // handle the error
  });
}
```

### Personalizar flows que aparecen más adelante \{#customize-flows-shown-later\}

Las respuestas del cuestionario también pueden decidir qué ve un usuario en un [placement](placements) posterior — un flow diferente, o un paywall distinto dentro de él.

Por ejemplo, pregunta a los usuarios sobre su experiencia con el deporte en tu flow de onboarding y luego muestra a cada grupo su propio flow con diferentes productos y textos.

1. Añade un [cuestionario](onboarding-quizzes) a tu flow. Dale al [grupo seleccionable](flow-selectable-elements) el ID de grupo `experience`, y a cada opción un ID de elemento significativo.
2. Gestiona las respuestas y [establece atributos personalizados](react-native-setting-user-attributes) para el usuario.

```javascript showLineNumbers title="React Native"
function handleFlowInput(name, params) {
  if (name !== 'flow_user_input') return;
  if (params.element_id !== 'experience') return;

  adapty
    .updateProfile({
      // Set the custom attribute 'experience' to the option the user selected
      // (beginner, amateur, or pro).
      codableCustomAttributes: { experience: params.item_ids[0] },
    })
    .catch(error => {
      // handle the error
    });
}
```

3. [Crea un segmento](segments) para cada valor de atributo personalizado.
4. Crea un [placement](placements) y añade una [audiencia](audience) para cada segmento.
5. [Muestra el flow](react-native-present-paywalls) de ese placement en tu app.