如何在Headless UI的ListBox组件onChange时获取选中数组索引?
解决Headless UI ListBox获取选中项数组索引的问题
不用自己给Listbox.Option绑定点击或键盘事件——Headless UI的Listbox组件已经封装了完整的交互逻辑(包括键盘导航、选中切换),你绑定的keydown事件不会生效,因为组件内部已经拦截并处理了这些按键操作。直接利用组件提供的onChange回调就能拿到索引,以下两种方案都可行:
方案一:保持选中值为对象,同步计算索引
如果需要同时保留选中对象和索引,可以在onChange里通过数组方法找到对应索引:
export default function Example() { const [selected, setSelected] = useState(people[0]); const [selectedIndex, setSelectedIndex] = useState(0); // 新增索引状态 const handleSelect = (newSelected) => { setSelected(newSelected); // 用唯一标识(比如id)查找索引,比name更可靠 const idx = people.findIndex(person => person.id === newSelected.id); setSelectedIndex(idx); }; return ( <div className="fixed top-16 w-72"> <Listbox value={selected} onChange={handleSelect}> <div className="relative mt-1"> <Listbox.Button className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm"> <span className="block truncate">{selected.name}</span> <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> </span> </Listbox.Button> <Transition as={Fragment} leave="transition ease-in duration-100" leaveFrom="opacity-100" leaveTo="opacity-0" > <Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> {people.map((person, personIdx) => ( <Listbox.Option key={personIdx} className={({ active }) => `relative cursor-default select-none py-2 pl-10 pr-4 ${active ? 'bg-amber-100 text-amber-900' : 'text-gray-900'}` } value={person} > {({ selected }) => ( <> <span className={`block truncate ${selected ? 'font-medium' : 'font-normal'}`} > {person.name} </span> {selected ? ( <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600"> <CheckIcon className="h-5 w-5" aria-hidden="true" /> </span> ) : null} </> )} </Listbox.Option> ))} </Listbox.Options> </Transition> </div> </Listbox> </div> ) }
方案二:直接将选中值设为索引(更简洁)
如果只需要索引,或者可以通过索引获取对象,直接把Listbox的value设为数组索引,onChange返回的就是选中项的索引:
export default function Example() { const [selectedIndex, setSelectedIndex] = useState(0); const selected = people[selectedIndex]; // 通过索引获取选中对象 return ( <div className="fixed top-16 w-72"> <Listbox value={selectedIndex} onChange={setSelectedIndex}> <div className="relative mt-1"> <Listbox.Button className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm"> <span className="block truncate">{selected.name}</span> <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> </span> </Listbox.Button> <Transition as={Fragment} leave="transition ease-in duration-100" leaveFrom="opacity-100" leaveTo="opacity-0" > <Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> {people.map((person, personIdx) => ( <Listbox.Option key={personIdx} className={({ active }) => `relative cursor-default select-none py-2 pl-10 pr-4 ${active ? 'bg-amber-100 text-amber-900' : 'text-gray-900'}` } value={personIdx} // 这里把value设为索引 > {({ selected }) => ( <> <span className={`block truncate ${selected ? 'font-medium' : 'font-normal'}`} > {person.name} </span> {selected ? ( <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600"> <CheckIcon className="h-5 w-5" aria-hidden="true" /> </span> ) : null} </> )} </Listbox.Option> ))} </Listbox.Options> </Transition> </div> </Listbox> </div> ) }
两种方案都能完美适配点击、空格/回车选中的场景,不用自己处理键盘事件,完全利用组件原生的交互逻辑。
内容的提问来源于stack exchange,提问作者M S
相关产品推荐
相关产品推荐

