Next.js 14中useState不生效?自定义Select组件状态异常
自定义Select组件useState不更新的解决方案
核心问题分析与修复
1. Select.Option未绑定点击事件
你的SelectOption组件没有将接收的props(包括onClick)传递给内部DOM元素,导致点击选项时handleSelect函数完全不会触发——这是状态不更新的根本原因。
修改SelectOption组件,把props传递给内部div:
import { OptionProps } from '@/components/global-types'; import { twMerge } from 'tailwind-merge'; export default function SelectOption({ children, className, ...props }: OptionProps) { return ( <div className={twMerge('flex items-center justify-between cursor-pointer hover:bg-zinc-700 text-white h-8 px-2 whitespace-nowrap rounded', className)} {...props} // 关键:传递所有props,包括onClick > {children} </div> ); }
2. 点击选项后下拉框未可靠关闭(优化项)
当前点击选项后,事件会冒泡到SelectRoot的div,触发开关状态反转,但这种方式依赖事件冒泡,逻辑不够清晰。可以主动控制下拉框关闭:
第一步:修改SelectRoot组件,添加开关回调
'use client'; import { SelectProps } from '@/components/global-types'; import { ChevronDown, ChevronUp } from 'lucide-react'; import { useState } from 'react'; import { twMerge } from 'tailwind-merge'; export default function SelectRoot({ className, children, selected, onToggle, ...props }: SelectProps & { onToggle?: (open: boolean) => void }) { const [open, setOpen] = useState(false); const handleToggle = () => { const newState = !open; setOpen(newState); onToggle?.(newState); }; return ( <div className={twMerge('group rounded relative cursor-pointer h-8 m-5 px-2 flex items-center flex-row-reverse justify-between bg-white', className)} onClick={handleToggle} {...props} > {open ? <ChevronUp className="group-hover:opacity-70" /> : <ChevronDown className="group-hover:opacity-70" />} {selected} {open && children} </div> ); }
第二步:在Ferramentas组件中主动控制关闭
'use client'; import Image from 'next/image'; import { Select } from '@/components/ui/select'; import { ferramentas } from '@/libs/ferramentas'; import { useState } from 'react'; import { Check } from 'lucide-react'; export default function Ferramentas() { const [tool, setTool] = useState(''); const [selectOpen, setSelectOpen] = useState(false); const handleSelect = (option: string) => { setTool(option); setSelectOpen(false); // 选中后直接关闭下拉框 }; // 优化:显示选中工具的名称而非ID const selectedTool = ferramentas.find(f => f.item_id === tool); return ( <div className="ml-16"> <Select.Root className="w-[500px]" selected={selectedTool?.name || '请选择工具'} onToggle={setSelectOpen} > <Select.Content> {ferramentas.map((ferramenta) => ( <Select.Option key={ferramenta.item_id} onClick={() => handleSelect(ferramenta.item_id)}> <div className="flex items-center gap-3"> <Image alt={ferramenta.name} src={`https://render.albiononline.com/v1/item/${ferramenta.item_id}.png`} width={32} height={32} /> {ferramenta.name} </div> {tool === ferramenta.item_id ? <Check /> : null} </Select.Option> ))} </Select.Content> </Select.Root> <h1>{selectedTool?.name || tool}</h1> </div> ); }
额外说明
- 修复第一个问题后,点击选项时
handleSelect会正常触发,tool状态也会更新; - 第二个优化项是为了让下拉框的关闭逻辑更可控,避免冒泡带来的意外问题;
- 最后添加的显示名称逻辑是提升用户体验的小改进,可根据需求调整。
内容的提问来源于stack exchange,提问作者Dayvson Spacca
相关产品推荐
相关产品推荐

