Next.js多选组件点击触发TypeError错误求助
解决 "undefined is not iterable" 错误及多选组件问题
核心错误原因
点击组件时出现的错误,本质是尝试对非数组类型(undefined)执行遍历操作。主要触发点在 MultiSelect.tsx 中的 collections.map 调用——如果 collections 状态不是数组,就会抛出这个错误。
具体问题分析
- API 响应处理不严谨:在
ProductForm.tsx的getCollections函数中,直接将 API 返回的data赋值给collections,但未校验返回值是否为数组。如果 API 返回错误(比如 404/500)或非数组格式(比如错误信息对象),collections会变成 undefined 或非数组,导致后续map失败。 - 表单默认值字段不匹配:表单 schema 定义的是
size: z.array(z.string()),但默认值里写的是sizes: [],字段名不匹配会导致size字段初始值为 undefined,后续可能引发其他遍历错误。 - Command 组件结构不符合规范:shadcn/ui 的
Command组件需要特定的层级结构(比如CommandDialog包裹列表),当前MultiSelect的结构可能导致内部状态异常。
修复方案
1. 确保 collections 始终为数组
修改 ProductForm.tsx 中的 getCollections 函数,添加数组校验:
const getCollections = async () => { try{ setLoading(true) const res = await fetch("/api/collections", { method: "GET" }) const data = await res.json() // 确保返回值是数组,否则设为空数组 setCollections(Array.isArray(data) ? data : []) setLoading(false) }catch(err) { console.log("[collections_GET]", err) toast.error("Something went wrong. Please try again.") // 出错时也重置为空数组 setCollections([]) setLoading(false) } }
2. 修正表单默认值字段名
在 ProductForm.tsx 的表单默认值中,把 sizes: [] 改为 size: [],和 schema 保持一致:
defaultValues: initialData ? initialData : { title: "", description: "", media: [], category: "", collections: [], tags: [], size: [], // 这里改成 size,不是 sizes colors: [], price: 0.1, expense: 0.1, },
3. 调整 MultiSelect 的 Command 组件结构
按照 shadcn/ui 的规范,使用 CommandDialog 和 CommandList 包裹选项列表,避免内部状态异常:
// MultiSelect.tsx import { useState } from "react"; import { Command, CommandDialog, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; interface MultiSelectProps { placeholder: string collections: CollectionType[] value: string[] onChange: (value: string) => void onRemove: (value: string) => void } const MultiSelect: React.FC<MultiSelectProps> = ({ placeholder, collections, value, onChange, onRemove, }) => { const [inputValue, setInputValue] = useState(""); const [open, setOpen] = useState(false); return ( <Command> <CommandInput placeholder={placeholder} value={inputValue} onValueChange={setInputValue} onBlur={() => setOpen(false)} onFocus={() => setOpen(true)} /> <CommandDialog open={open}> <CommandList> <CommandGroup> {collections.map((collection) => ( <CommandItem key={collection._id} onClick={() => onChange(collection._id)} // 添加点击触发选中逻辑 > {collection.title} </CommandItem> ))} </CommandGroup> </CommandList> </CommandDialog> </Command> ); }; export default MultiSelect;
额外优化:处理已选中项的显示
当前 MultiSelect 没有展示已选中的集合,建议添加标签展示选中项,并绑定移除逻辑:
// 在 MultiSelect 的 return 中,CommandInput 上方添加选中标签 return ( <div className="space-y-2"> {/* 已选中标签 */} <div className="flex flex-wrap gap-2"> {value.map((id) => { const collection = collections.find(c => c._id === id); return collection ? ( <div key={id} className="flex items-center gap-1 bg-gray-100 px-2 py-1 rounded"> <span>{collection.title}</span> <button onClick={() => onRemove(id)} className="text-gray-500">×</button> </div> ) : null; })} </div> {/* 原有的 Command 组件 */} <Command> {/* ... 保持原有代码 ... */} </Command> </div> );
内容的提问来源于stack exchange,提问作者Jey-Es
相关产品推荐
相关产品推荐

