Skip to content

react

import { useThaiAddressAutocomplete } from 'thaizip/react'

function useThaiAddressAutocomplete(options: UseThaiAddressAutocompleteOptions): {
query: string
setQuery: (value: string) => void
setQuerySilent: (value: string) => void
suggestions: ThaiAddressSuggestion[]
isOpen: boolean
selectSuggestion: (item: ThaiAddressSuggestion) => ResolvedThaiAddress | null
clear: () => void
}

A headless hook that wraps searchThaiAddress with a built-in debounce and manages query/suggestions state for you. react/react-dom are optional peer dependencies, and the built files already carry a "use client" directive, so it works in a Next.js App Router project without any extra wrapper.

Parameters (UseThaiAddressAutocompleteOptions)

Section titled “Parameters (UseThaiAddressAutocompleteOptions)”
NameTypeDefaultDescription
indexTrigramIndex— (required)The index to search. Must not be null/undefined when the hook is called — hooks can’t be called conditionally, so if your index loads asynchronously, split off an outer component that waits for it first
limit?number10Forwarded to searchThaiAddress as-is — caps the number of results for text queries
debounce?number200Delay, in milliseconds, before a search actually runs after the user stops typing
threshold?number0.4Forwarded to searchThaiAddress as-is — the minimum score that counts as a match
zipLimit?numberundefinedForwarded to searchThaiAddress as-is (the hook itself sets no default — if omitted, searchThaiAddress’s own default of Infinity applies)
initialQuery?string''Seed value for query on first render. Does not trigger a search — useful for pre-filling the input with an already-chosen address (e.g. react-hook-form defaultValues) without popping the dropdown open
locale?AddressLocale ('th' | 'en')undefinedLanguage for the label field of returned suggestions, forwarded to formatThaiAddressSuggestion
onSelect?(address: ResolvedThaiAddress) => voidundefinedCalled after selectSuggestion successfully resolves an address
NameTypeDescription
querystringThe current text in the search input
setQuery(value: string) => voidSets a new query — triggers a debounced search (debounce ms after the last keystroke)
setQuerySilent(value: string) => voidSets the query without re-searching or reopening the dropdown — use it to echo a chosen label back into the input after selectSuggestion, or to seed the query after mount (e.g. once async defaultValues resolve)
suggestionsThaiAddressSuggestion[]The array of results from the most recent search
isOpenbooleanquery.length > 0 && suggestions.length > 0 — use this to decide whether to render the dropdown
selectSuggestion(item: ThaiAddressSuggestion) => ResolvedThaiAddress | nullTakes a full suggestion object (from suggestions, not just its id), looks up the original record by item.id internally in O(1), resolves it into a ResolvedThaiAddress, clears suggestions, fires onSelect, and returns the resolved address — returns null (never throws) if item.id doesn’t match the latest batch of suggestions (e.g. it’s stale). It deliberately does not touch query — call setQuerySilent/clear yourself if you also want to update the input
clear() => voidResets query and suggestions back to empty
  • index is read via an internal ref — a new object reference passed in on each render does not restart a pending debounce timer. But if index actually changes value (e.g. swapped for a different dataset) while a query is pending, the hook re-searches immediately with the new index, without waiting for the debounce
  • initialQuery and a non-empty setQuerySilent('...') both suppress exactly the next search that would otherwise fire — this covers both the debounced effect and the effect that reacts to index changing (both run together on mount). setQuerySilent('') (an empty value) does not arm this suppression, so it can’t accidentally swallow the next real keystroke
  • query.length === 0 clears suggestions immediately, without waiting for the debounce
import { useState } from 'react'
import { useThaiAddressAutocomplete } from 'thaizip/react'
import type { ResolvedThaiAddress, TrigramIndex } from 'thaizip'
function AddressAutocomplete({ index }: { index: TrigramIndex }) {
const [selected, setSelected] = useState<ResolvedThaiAddress | null>(null)
const { query, setQuery, setQuerySilent, suggestions, isOpen, selectSuggestion } =
useThaiAddressAutocomplete({ index, onSelect: setSelected })
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
{isOpen && (
<ul>
{suggestions.map((s) => (
<li
key={s.id}
onClick={() => {
const resolved = selectSuggestion(s)
if (resolved) setQuerySilent(s.label)
}}
>
{s.label} ({s.zipCode})
</li>
))}
</ul>
)}
</div>
)
}