Docs navigation

useCancelFlow

The headless hook. Returns the cancel flow's state and actions as React state, leaving rendering to you.

import { useCancelFlow } from '@churnkey/react/headless'
 
const flow = useCancelFlow(config: FlowConfig)

Configuration

useCancelFlow accepts the same FlowConfig as <CancelFlow> minus the rendering props (appearance, classNames, components). Everything else — steps, customer, subscriptions, session, appId, mode, apiBaseUrl, all handle<Type> and on<Type> callbacks, and customComponents — passes through unchanged.

State

FieldTypeWhat it is
step'survey' | 'offer' | 'feedback' | 'confirm' | 'success' | stringThe current step type. Custom types come through as their string value.
currentStepResolvedStep | undefinedThe resolved step config (type, title, description, offer, losses, data, etc.). undefined only in connected mode while the config is still loading.
stepIndexnumberZero-based position of the current step in the resolved flow.
totalStepsnumberTotal resolved steps. Useful for progress UIs (stepIndex / totalSteps).
reasonsReasonConfig[]The current survey's reasons. Empty when not on a survey step.
selectedReasonstring | nullThe reason ID the customer picked.
currentOfferOfferDecision | nullThe offer on screen. null outside offer steps.
feedbackstringThe feedback textarea value.
followupResponsestringText entered into the follow-up textarea when a freeform: true reason is selected. Empty otherwise.
outcome'saved' | 'cancelled' | nullThe terminal state. null until the flow completes.
isProcessingbooleantrue while a handler or server action is in flight.
errorError | nullThe last action's error, if any. Cleared on retry.
customerDirectCustomer | nullThe customer in the flow's state — the customer prop in open source / analytics, or the customer resolved from the token in connected mode. null only when nothing was supplied.
subscriptionsDirectSubscription[]The subscriptions passed to the hook, or empty.
isLoadingbooleanConnected mode only. true while fetching the dashboard-configured flow.
loadErrorError | nullConnected mode only. The fetch error, if any.

Actions

FunctionSignatureWhat it does
selectReason(id: string) => voidPick a survey reason. Updates selectedReason.
next(result?: object) => voidAdvance to the next step. result is captured for custom steps.
back() => voidGo back. No-op on the first step.
accept(result?: object) => Promise<void>Accept the current offer. Triggers handle<Type> or the server action.
decline() => voidDecline the current offer; advance past it.
cancel() => Promise<void>Confirm cancellation. Triggers handleCancel or the server action.
close() => voidClose the flow. Records as abandoned if mid-progress.
setFeedback(text: string) => voidUpdate the feedback value.
setFollowupResponse(text: string) => voidUpdate the follow-up text for the active freeform: true reason.
retry() => voidConnected mode only. Re-fetch the dashboard-configured flow after loadError.

Return shape (typed)

interface UseCancelFlowReturn {
  // State
  step:             'survey' | 'offer' | 'feedback' | 'confirm' | 'success' | string
  currentStep:      ResolvedStep | undefined
  stepIndex:        number
  totalSteps:       number
  reasons:          ReasonConfig[]
  selectedReason:   string | null
  currentOffer:     OfferDecision | null
  feedback:         string
  followupResponse: string
  outcome:          'saved' | 'cancelled' | null
  isProcessing:     boolean
  error:            Error | null
  customer:         DirectCustomer | null
  subscriptions:    DirectSubscription[]
 
  // Actions
  selectReason:        (id: string) => void
  next:                (result?: Record<string, unknown>) => void
  back:                () => void
  accept:              (result?: Record<string, unknown>) => Promise<void>
  decline:             () => void
  cancel:              () => Promise<void>
  close:               () => void
  setFeedback:         (text: string) => void
  setFollowupResponse: (text: string) => void
 
  // Connected mode extras
  isLoading: boolean
  loadError: Error | null
  retry:     () => void
}

Minimal usage

import { useCancelFlow } from '@churnkey/react/headless'
 
function CancelPage() {
  const flow = useCancelFlow({
    steps: [/* ... */],
    handleCancel: async () => myBilling.cancel(),
  })
 
  if (flow.isLoading)  return <Spinner />
  if (flow.loadError)  return <ErrorBanner onRetry={flow.retry} />
 
  switch (flow.step) {
    case 'survey':   return <MySurvey flow={flow} />
    case 'offer':    return <MyOffer  flow={flow} />
    case 'feedback': return <MyFeedback flow={flow} />
    case 'confirm':  return <MyConfirm flow={flow} />
    case 'success':  return <MySuccess flow={flow} />
    default:         return <MyCustomStep flow={flow} type={flow.step} />
  }
}

For a complete walkthrough, see Headless.

Next steps