基于React HeadlessUI实现带‘全选’功能的单选下拉选择组件的技术咨询
基于React HeadlessUI实现带‘全选’功能的单选下拉选择组件的技术咨询
Hey 各位前端小伙伴,最近我在用Headless UI的Listbox组件实现一个带单选逻辑的下拉选择器,核心需求是要支持“全选”选项——当用户选了“全选”时,需要把所有可选项的code都作为数组传给父组件;选单个选项的话就只传该选项的code。这个组件后续要用来做国家和语言选择,下面是我目前写的实现代码,想请大家帮忙看看有没有可以优化的地方,或者有没有我没考虑到的边界情况?
import { Listbox } from '@headlessui/react'; import { ChevronDown } from 'lucide-react'; import { Fragment } from 'react'; interface Option { name: string; code: string; } interface RadioSingleSelectProps { options: Option[]; selectedCodes: string[]; onChange: (codes: string[]) => void; placeholder?: string; } export default function RadioSingleSelect({ options, selectedCodes, onChange, placeholder = 'Select an option', }: RadioSingleSelectProps) { const selectOptions: Option[] = [ { name: 'Select All', code: '__ALL__' }, ...options, ]; const allCodes = options.map((o) => o.code); const isAllSelected = selectedCodes.length === allCodes.length; const selectedCode = isAllSelected ? '__ALL__' : selectedCodes[0]; const selectedOption = selectOptions.find((opt) => opt.code === selectedCode) || { name: placeholder, code: '' }; const handleChange = (option: Option) => { if (option.code === '__ALL__') { onChange(allCodes); } else { onChange([option.code]); } }; return ( <div className="w-full text-sm"> <Listbox value={selectedOption} onChange={handleChange}> <div className="relative"> <Listbox.Button className="relative w-full cursor-pointer rounded-md bg-gray-100 py-2 pl-4 pr-10 text-left border border-gray-300 h-12 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"> <span className="block truncate font-normal text-gray-800"> {selectedOption.code ? selectedOption.name : placeholder} </span> <span className="pointer-events-none absolute inset-y-0 right-3 flex items-center"> <ChevronDown className="h-4 w-4 text-gray-400" /> </span> </Listbox.Button> <Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white border border-gray-200 shadow-lg z-50"> {selectOptions.map((option) => ( <Listbox.Option key={option.code} value={option} as={Fragment}> {({ selected, active }) => ( <li className={`relative cursor-pointer select-none py-2 pl-10 pr-4 text-sm ${ active ? 'bg-purple-100 text-purple-900' : 'text-gray-900' }`} > <span className={`block truncate ${ selected ? 'font-medium' : 'font-normal' }`} > {option.name} </span> {selected && ( <span className="absolute inset-y-0 left-3 flex items-center"> <input type="radio" checked={selected} readOnly className="h-4 w-4 text-purple-600" /> </span> )} </li> )} </Listbox.Option> ))} </Listbox.Options> </div> </Listbox> </div> ); }
我大概的实现逻辑是这样的:
- 先在原选项数组前面加了一个“Select All”的选项,用
__ALL__作为标记code - 提前计算好所有选项的code数组,通过判断选中的code长度是否等于总选项数,来确定是否处于全选状态
- 处理选择事件时,如果点击的是全选选项,就把所有code数组传给
onChange;否则只传当前选中的单个code - 下拉按钮的显示内容会根据选中状态自动切换:全选时显示“Select All”,单选时显示对应选项名称,未选中任何内容时显示占位符
内容来源于stack exchange
相关产品推荐
相关产品推荐

