You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js多选组件点击触发TypeError错误求助

解决 "undefined is not iterable" 错误及多选组件问题

核心错误原因

点击组件时出现的错误,本质是尝试对非数组类型(undefined)执行遍历操作。主要触发点在 MultiSelect.tsx 中的 collections.map 调用——如果 collections 状态不是数组,就会抛出这个错误。

具体问题分析

  1. API 响应处理不严谨:在 ProductForm.tsx 的 getCollections 函数中,直接将 API 返回的 data 赋值给 collections,但未校验返回值是否为数组。如果 API 返回错误(比如 404/500)或非数组格式(比如错误信息对象),collections 会变成 undefined 或非数组,导致后续 map 失败。
  2. 表单默认值字段不匹配:表单 schema 定义的是 size: z.array(z.string()),但默认值里写的是 sizes: [],字段名不匹配会导致 size 字段初始值为 undefined,后续可能引发其他遍历错误。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 01:15:58