Skip to content

useAsyncOptions

useAsyncOptions fetches a list of { label, value } options for use in selects, dropdowns, and comboboxes. It wraps the fetcher with reactive options, loading, error, and reload state.

Signature

ts
interface SelectOption<T = string | number> {
  label: string
  value: T
  disabled?: boolean
  [key: string]: unknown
}

function useAsyncOptions<T = string | number>(
  fetcher: () => Promise<SelectOption<T>[]>,
  opts?: { immediate?: boolean }
): {
  options: Ref<SelectOption<T>[]>
  loading: Ref<boolean>
  error: Ref<string | null>
  reload: () => Promise<void>
}

Basic usage

ts
import { useAsyncOptions } from '@jetpack-labs/jetpack-ui'
import api from '@/axios'

const { options: productOptions, loading } = useAsyncOptions(() =>
  api.get('/api/v1/products').then(r =>
    r.data.map(p => ({ label: p.name, value: p.id }))
  )
)
vue
<template>
  <select v-if="!loading">
    <option
      v-for="opt in productOptions"
      :key="opt.value"
      :value="opt.value"
      :disabled="opt.disabled"
    >
      {{ opt.label }}
    </option>
  </select>
</template>

With typed value

Use the generic parameter for non-string/number values:

ts
interface Customer { id: number; name: string }

const { options } = useAsyncOptions<number>(() =>
  api.get('/api/v1/customers').then(r =>
    r.data.map((c: Customer) => ({ label: c.name, value: c.id }))
  )
)
// options is Ref<SelectOption<number>[]>

Deferred fetch

ts
const { options, reload } = useAsyncOptions(
  () => api.get('/api/v1/cost-centers').then(r =>
    r.data.map(c => ({ label: c.name, value: c.id }))
  ),
  { immediate: false }
)

// trigger when needed
onMounted(() => reload())

Disabled options

Include disabled: true in any option object to disable that option:

ts
const { options } = useAsyncOptions(() =>
  api.get('/api/v1/shifts').then(r =>
    r.data.map(s => ({
      label: s.name,
      value: s.id,
      disabled: s.locked,
    }))
  )
)

Error handling

If the fetcher throws, error is set and options resets to an empty array []. The error is cleared on the next reload().

Return values

KeyTypeDescription
optionsRef<SelectOption<T>[]>The resolved option list (empty array while loading)
loadingRef<boolean>true while the fetcher is running
errorRef<string | null>Error message, or null
reload() => Promise<void>Re-runs the fetcher

SelectOption interface

ts
interface SelectOption<T = string | number> {
  label: string          // display text
  value: T               // bound value
  disabled?: boolean     // disables the option
  [key: string]: unknown // extra metadata passthrough
}

This interface is exported from the package:

ts
import type { SelectOption } from '@jetpack-labs/jetpack-ui'

Private — Jetpack Labs internal use only