# useAdaptiveCard URL: /docs/native/hooks/use-adaptive-card Headless state, validation, visibility and action dispatch for the native Adaptive Cards renderer. Drives the AdaptiveCard component and is exported so apps can wire custom toolbars or server-side validation. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-adaptive-card ``` **Dependencies:** - [react](https://react.dev) The hook ships with the [rn-adaptive-card](/docs/native/docyrus/adaptive-card) component (it imports the card's types and `lib/` helpers). It is also re-exported from `@/components/docyrus-native/adaptive-card`, and the old `components/adaptive-card/use-adaptive-card` path still resolves. ## Overview `useAdaptiveCard` owns the runtime state of one card. The public API matches web `@docyrus/ui` 1:1. - **Inputs.** Every visible `Input.*` element is registered by `id`, with its live value and validation state. - **Visibility overrides.** `Action.ToggleVisibility` results are layered on top of the static `isVisible` defaults. - **Show-card state.** Each `Action.ShowCard` button has an open or closed flag. - **Action dispatch.** `submit`, `execute`, `openUrl`, `toggleVisibility`, `showCard` and `resetInputs` are emitted as typed events. - **Submit payload assembly.** The hook walks the visible tree, honours `associatedInputs: 'auto' | 'none'` and merges `action.data`. - **Focus on the first invalid input.** When a submit or execute fails validation, the hook focuses the first invalid input. The native equivalent of the web `[aria-invalid]` DOM lookup is a focus registry: `Input.Text` and `Input.Number` register their `TextInput` ref, so the keyboard opens on that field and the enclosing scroll view brings it into view. `` uses this hook internally. Call it yourself only when you need one of these: - Trigger actions programmatically, for example from a native header button. - Read input state from outside the card. - Wire server-side validation that returns errors per input id. ## Usage ### Custom integration ```tsx import { Button, View } from 'react-native'; import { AdaptiveCardView, useAdaptiveCard, type AdaptiveCardActionEvent, type AdaptiveCardPayload } from '@/components/docyrus-native/adaptive-card'; export function CardWithToolbar({ payload }: { payload: AdaptiveCardPayload }) { function handleAction(event: AdaptiveCardActionEvent) { console.log(event); } const adaptive = useAdaptiveCard(payload, { onAction: handleAction }); return ( ); } ``` ### Reading the current input state ```tsx const adaptive = useAdaptiveCard(payload); console.log(adaptive.state.inputs.email); console.log(adaptive.state.validations.email); ``` ### Submitting programmatically ```tsx const submitAction = payload.actions?.find(a => a.type === 'Action.Submit'); if (submitAction && submitAction.type === 'Action.Submit') { adaptive.controls.submit(submitAction); } ``` ### Resetting after a successful save ```tsx async function handleAction(event: AdaptiveCardActionEvent) { if (event.type !== 'submit') return; await api.post('/forms', event.data); adaptive.controls.reset(); // clears inputs, visibility overrides and show-card state } ``` ## API Reference ```ts function useAdaptiveCard(card: AdaptiveCardPayload, options?: UseAdaptiveCardOptions): UseAdaptiveCardReturn; ``` ### `UseAdaptiveCardOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `onAction` | `(event: AdaptiveCardActionEvent) => void \| Promise` | — | Receives every dispatched action. | | `hostConfig` | `AdaptiveCardHostConfigOverride` | — | Deep-merged onto `defaultHostConfig`. | | `customElements` | `Record` | `{}` | Renderers for unknown `type` strings. | | `onChoiceQuery` | `(request: AdaptiveCardChoiceQueryRequest) => Promise>` | — | Async resolver for a filtered `Input.ChoiceSet` whose `choices.data` is a `Data.Query`. The input is debounced, and the returned choices are merged with the static `choices`. A throwing resolver yields `[]`. | ### `AdaptiveCardChoiceQueryRequest` | Field | Type | Description | |-------|------|-------------| | `dataset` | `string` | The `Data.Query` dataset name. | | `search` | `string` | The current filter text. | | `inputId` | `string` | The `Input.ChoiceSet` id. | ### `UseAdaptiveCardReturn` | Field | Type | Description | |-------|------|-------------| | `card` | `AdaptiveCardPayload` | The payload passed in. | | `hostConfig` | `AdaptiveCardHostConfig` | The resolved host config: the default deep-merged with your override. | | `state.inputs` | `Record` | Live input values, keyed by `id`. | | `state.validations` | `Record` | Validation for each input (`{ isValid, errorMessage? }`), recomputed on every change. | | `state.visibilityOverrides` | `Record` | `Action.ToggleVisibility` overrides, keyed by element `id`. | | `state.showCardOpen` | `Record` | Open flags for `Action.ShowCard`, keyed by the action's `id` or `title`. | | `state.hasSubmittedOnce` | `boolean` | Becomes `true` after the first failed submit, which is when per-input errors start to show. | | `controls.setInput(id, value)` | `(id, value) => void` | Sets an input imperatively. | | `controls.submit(action)` | `(action: AdaptiveCardActionSubmit) => void` | Validates, then dispatches `'submit'`. On failure, focuses the first invalid input instead. | | `controls.execute(action)` | `(action: AdaptiveCardActionExecute) => void` | Validates, then dispatches `'execute'` with `verb`. On failure, focuses the first invalid input instead. | | `controls.openUrl(action)` | `(action: AdaptiveCardActionOpenUrl) => void` | Dispatches `'openUrl'`. | | `controls.toggleVisibility(action)` | `(action: AdaptiveCardActionToggleVisibility) => void` | Applies the visibility overrides, then dispatches `'toggleVisibility'`. | | `controls.toggleShowCard(action)` | `(action: AdaptiveCardActionShowCard) => void` | Flips a show-card, then dispatches `'showCard'`. | | `controls.resetInputs(action)` | `(action: AdaptiveCardActionResetInputs) => void` | Clears the target inputs (all of them when `targetInputIds` is empty), then dispatches `'resetInputs'`. | | `controls.validateAll()` | `() => { ok; firstInvalidId?; errors }` | Runs validation immediately. It does not dispatch or change focus. | | `controls.reset()` | `() => void` | Clears inputs, visibility overrides and show-card state. | | `cardProps` | `AdaptiveCardContextValue` | The value `` consumes. On native it also carries `registerInputFocus` for the focus registry. | ## When validation runs - **The first submit** validates silently. If any input fails, the hook sets `state.hasSubmittedOnce = true`, focuses the first invalid focusable input, and does **not** dispatch. - **After that failed submit**, validation re-renders live, so each error message clears once the user fixes the field. - **Date, time, toggle, choice-set and rating inputs** have no text caret to focus. They show their error state but are not focused. ## The `associatedInputs` contract `Action.Submit` and `Action.Execute` build the submit payload from the **currently visible** card tree: - **`'auto'` (default):** collects every `Input.*` that is visible under the current `visibilityOverrides`. Hidden inputs are left out. - **`'none'`:** skips inputs and emits only the literal `action.data`. ## Type Exports | Type | Description | |------|-------------| | `UseAdaptiveCardOptions` | The hook options. | | `UseAdaptiveCardReturn` | The full hook return value. | | `AdaptiveCardChoiceQueryRequest` | The request passed to `onChoiceQuery`. | | `AdaptiveCardInputValue` | `string \| Array \| boolean \| number \| null`. |