react-thaizip

เลือกที่อยู่แบบลำดับชั้น

เลือกจังหวัด อำเภอหรือเขต และตำบลหรือแขวงตามลำดับ

ThaiAddressCascadeSelect เหมาะกับฟอร์มที่ต้องการให้ผู้ใช้เลือกที่อยู่ตามลำดับ จังหวัด → อำเภอ/เขต → ตำบล/แขวง

จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์
'use client'

import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectDemo() {
  return <ThaiAddressCascadeSelect onValueChange={(address) => console.log(address)} />
}

ติดตั้ง

npx react-thaizip add cascade-select

CLI เลือกไฟล์ให้ตาม style ที่ init บันทึกไว้: โปรเจกต์ที่มี components.json ของ shadcn/ui จะได้ไฟล์ที่ประกอบจาก primitives ของ shadcn (Base UI, Radix หรือ React Aria) นอกนั้นได้แบบ vanilla

วิธีใช้

import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'
<ThaiAddressCascadeSelect onValueChange={(address) => console.log(address)} />

onValueChange จะส่ง ResolvedThaiAddress | null เมื่อเลือกครบทั้งสามระดับ

เมื่อเปลี่ยนตัวเลือกหลัก

เมื่อเลือกที่อยู่ครบแล้ว การเปลี่ยนจังหวัดหรืออำเภอ/เขตจะเรียก onValueChange(null) และล้างตัวเลือกปลายทางที่ไม่สอดคล้องกัน ผู้ใช้จึงต้องเลือกตัวเลือกที่อยู่ถัดไปใหม่

ตัวอย่าง

เลือกข้อมูลภาษาอังกฤษ

ใช้ locale="en" เพื่อแสดงป้ายและตัวเลือกเป็นภาษาอังกฤษ

Province
District
Sub-district
Postal code
'use client'

import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectEn() {
  return <ThaiAddressCascadeSelect locale="en" onValueChange={(address) => console.log(address)} />
}

ควบคุมค่าจากภายนอก

ส่ง value เพื่อควบคุมค่าทั้งชุด หรือ defaultValue เพื่อตั้งลำดับที่เลือกไว้ตอนเริ่มต้น ตัวอย่างนี้แสดงค่า callback และมีปุ่มล้างค่า

จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์

ค่าปัจจุบัน

null
'use client'

import { useState } from 'react'
import type { ResolvedThaiAddress } from 'thaizip'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectControlled() {
  const [address, setAddress] = useState<ResolvedThaiAddress | null>(null)

  return (
    <div className="flex flex-col gap-3">
      <ThaiAddressCascadeSelect value={address} onValueChange={setAddress} />
      <div>
        <button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => setAddress(null)}>
          ล้างค่า
        </button>
      </div>
      <p className="text-sm font-medium">ค่าปัจจุบัน</p>
      <pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(address, null, 2)}</pre>
    </div>
  )
}

ปิดใช้งาน

ส่ง disabled เพื่อปิดทุก select

จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์
'use client'

import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectDisabled() {
  return <ThaiAddressCascadeSelect disabled />
}

ไม่ผ่านการตรวจสอบ

ส่ง aria-invalid เพื่อทำเครื่องหมายว่าไม่ผ่านการตรวจสอบทั้งสาม trigger

จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์
'use client'

import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectInvalid() {
  return <ThaiAddressCascadeSelect aria-invalid />
}

ปรับข้อความ

ใช้ texts เพื่อแทนที่ป้ายกำกับและ placeholder เช่นให้ตรงกับคำว่าเขต/แขวงของกรุงเทพฯ คีย์ที่ไม่ได้ส่งจะใช้ค่าเริ่มต้นตาม locale

จังหวัดที่จัดส่ง
เขต/อำเภอ
แขวง/ตำบล
รหัสไปรษณีย์
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function CascadeSelectTexts() {
  return (
    <ThaiAddressCascadeSelect
      texts={{
        provinceLabel: 'จังหวัดที่จัดส่ง',
        provincePlaceholder: 'เลือกจังหวัด',
        districtLabel: 'เขต/อำเภอ',
        subdistrictLabel: 'แขวง/ตำบล',
      }}
    />
  )
}

ใช้ในฟอร์ม

ส่ง name เพื่อส่งค่าที่เลือกไปกับฟอร์มปกติ คอมโพเนนต์จะสร้าง hidden inputs สี่ช่อง (address-subdistrict, address-district, address-province, address-zipcode) และ required จะกันไม่ให้ส่งฟอร์มจนกว่าจะเลือกครบทั้งสามระดับ ดูรูปแบบอื่นได้ที่ การใช้งานร่วมกับฟอร์ม

จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์

FormData ที่ส่ง

null
'use client'

import { useRef, useState, type FormEvent } from 'react'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function FormCascadeSelect() {
  const ref = useRef<HTMLButtonElement>(null)
  const [payload, setPayload] = useState<Record<string, FormDataEntryValue> | null>(null)

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setPayload(Object.fromEntries(new FormData(event.currentTarget)))
  }

  return (
    <form className="flex flex-col gap-3" onSubmit={handleSubmit}>
      <ThaiAddressCascadeSelect
        ref={ref}
        name="address"
        required
      />
      <div className="flex gap-2">
        <button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => ref.current?.focus()}>
          โฟกัสช่องที่อยู่
        </button>
        <button type="submit" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent">
          ส่งฟอร์ม
        </button>
      </div>
      <p className="text-sm font-medium">FormData ที่ส่ง</p>
      <pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(payload, null, 2)}</pre>
    </form>
  )
}

API Reference

PropTypeค่าเริ่มต้นรายละเอียด
valueResolvedThaiAddress | null—ค่าที่ควบคุมอยู่; ส่ง null เพื่อล้าง
defaultValueResolvedThaiAddress | null—ค่าเริ่มต้นของโหมด uncontrolled และเลือกลำดับที่อยู่ให้ครบตั้งแต่เริ่ม
onValueChange(address: ResolvedThaiAddress | null) => void—เรียกเมื่อเลือกครบหรือเมื่อค่าถูกล้าง
namestring—สร้าง hidden inputs สี่ช่อง: <name>-subdistrict, -district, -province, -zipcode
locale'th' | 'en''th'ภาษาของป้ายและตัวเลือก
textsPartial<ThaiAddressCascadeSelectTexts>—แทนที่ข้อความ *Label/*Placeholder ของแต่ละระดับ รวมถึง loadingText, errorText, retryLabel
indexLoad'mount' | 'visible''mount'โหลดข้อมูลที่อยู่ตอน mount หรือรอจนช่องเลื่อนเข้ามาในจอ
disabledbooleanfalseปิดใช้งานทุก select
requiredboolean—กำหนดให้ทั้งสาม select ต้องมีค่า
aria-invalidReact.AriaAttributes['aria-invalid']—ทำเครื่องหมายว่าไม่ผ่านการตรวจสอบทั้งสาม trigger
onBlurReact.FocusEventHandler<HTMLButtonElement>—ทำงานเมื่อ trigger ของจังหวัดสูญเสียโฟกัส
onError(error: Error) => void—ทำงานเมื่อโหลดดัชนีที่อยู่ไม่สำเร็จ
classNamestring—grid wrapper หลัก
labelClassNamestring—ป้ายกำกับของทุกช่อง
triggerClassNamestring—trigger ของ select และช่องรหัสไปรษณีย์
popupClassNamestring—กล่องตัวเลือก
itemClassNamestring—แต่ละตัวเลือก
refReact.Ref<HTMLButtonElement>—ส่งต่อไปยัง trigger ของจังหวัด ซึ่งเป็นตัวควบคุมหลักของ cascade

ทั้ง ref และ onBlur ผูกกับ trigger ของจังหวัด ไม่ใช่ trigger ของอำเภอหรือตำบล

ดูเพิ่มเติม

ดู การใช้งานร่วมกับฟอร์ม สำหรับการส่งค่าที่อยู่ และ การปรับแต่ง สำหรับปรับหน้าตา ส่วนการค้นหาด้วยข้อความ ดู autocomplete

On this page