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

React中API获取讲师数据,react-select下拉框不显示选项的问题

问题描述

在AddCourse.tsx组件中,通过axios请求获取讲师列表并映射为包含id、value、label的options数组,console.log能正常输出该数组,但自定义的InputDropDown组件(基于react-select)的下拉框中无法显示这些选项。

相关代码

AddCourse.tsx中的数据获取与组件调用

const [instructorOptions, setInstructorOptions] = useState<
    Array<{ id: string; value: string; label: string }>
  >([]);

const fetchOptions = async () => {
    try {
      const response = await axios.get(`${API_URL}/instructor`);
      const instructorList: Array<{ id: string; name: string }> =
        response.data.data.instructor_list;

      if (instructorList.length > 0) {
        const options = instructorList.map((instructor) => ({
          id: instructor.id,
          value: instructor.name,
          label: instructor.name,
        }));
        setInstructorOptions(options);
        console.log('options : ', options);
      }
    } catch (error) {
      console.error('Error fetching instructors:', error);
      // Handle error, set appropriate state if needed
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchOptions();
  }, []);

  const handleSelect = (selectedOption: any) => {
    // 处理选中的选项,比如存入state
    console.log('选中的选项:', selectedOption);
  };
<div className="input-instructor">
  <InputDropDown
     options={instructorOptions}
     handleSelect={handleSelect}
  />
</div>

InputDropDown组件代码

import React, { useState } from 'react';
import Select from 'react-select';
import { BsTrash3 } from 'react-icons/bs';

function InputDropDown({ options, onSelect, text }: any) {
  const [optionsAvailable, setOptionsAvailable] = useState<string[]>(options);
  const [option, setOption] = useState<string[]>([]);
  const [selectedOption, setSelectedOption] = useState('');

  const handleSelectChange = (selectedOption: any) => {
    if (selectedOption && !option.includes(selectedOption)) {
      setOption((prevOptions) => [...prevOptions, selectedOption]);

      const updatedOptions = optionsAvailable.filter(
        (option: any) => option !== selectedOption,
      );

      setOptionsAvailable(updatedOptions);
    }
    setSelectedOption('');
  };

  const handleOptionRemove = (optionId: any) => {
    const updatedoptions = option.filter((item: any) => item.id !== optionId);
    console.log(updatedoptions);
    setOption(updatedoptions);

    // 更新可选选项
    const deletedOptions = options.filter((item: any) => item.id === optionId);
    setOptionsAvailable((prevOptions) => {
      const newOptions = [...prevOptions, ...deletedOptions];
      // 按id升序排序
      newOptions.sort((a, b) => a.id - b.id);
      return newOptions;
    });
  };

  return (
    <>
      <label className="mt-3 mb-3 block text-black dark:text-white">
        {text}
      </label>
      <Select
        value={selectedOption}
        onChange={handleSelectChange}
        options={optionsAvailable}
        isSearchable={true} // 启用搜索功能
        placeholder="请选择..."
      />
      <br />
      {option.map((data: any) => (
        <div className="flex mt-3 flex-row gap-2" key={data.id}>
          <div className="bg-primary text-white rounded-md px-2 py-1">
            {data.value}
          </div>
          <button
            onClick={() => handleOptionRemove(data.id)}
            className="bg-[#EF4444] text-white rounded-md px-2 py-1"
          >
            <BsTrash3 />
          </button>
        </div>
      ))}
    </>
  );
}

export default InputDropDown;

问题原因与修复方案

核心问题点

  1. 类型定义错误:optionsAvailable和option被错误定义为string[]类型,但实际传入的是对象数组,导致初始值解析异常。
  2. 未同步props更新:仅用useState初始化无法监听父组件传入的options变化(比如API请求完成后的新数据)。
  3. 对象比较逻辑失效:直接用option !== selectedOption比较对象,因对象引用不同导致过滤逻辑失效。
  4. Props名称不匹配:父组件传入handleSelect,但子组件接收的是onSelect,导致回调无法触发。
  5. SelectedOption类型错误:react-select的value属性应对应选项对象,而非字符串,导致选择状态异常。

修改后的InputDropDown组件代码

import React, { useState, useEffect } from 'react';
import Select from 'react-select';
import { BsTrash3 } from 'react-icons/bs';

// 统一选项类型,和父组件保持一致
type InstructorOption = {
  id: string;
  value: string;
  label: string;
};

function InputDropDown({ 
  options, 
  handleSelect, 
  text 
}: {
  options: InstructorOption[];
  handleSelect?: (option: InstructorOption) => void;
  text?: string;
}) {
  // 修正类型定义
  const [optionsAvailable, setOptionsAvailable] = useState<InstructorOption[]>([]);
  const [selectedItems, setSelectedItems] = useState<InstructorOption[]>([]);
  // react-select选中值应为单个选项对象
  const [selectedOption, setSelectedOption] = useState<InstructorOption | null>(null);

  // 监听props的options变化,同步更新可选选项
  useEffect(() => {
    const filteredOptions = options.filter(opt => 
      !selectedItems.some(item => item.id === opt.id)
    );
    setOptionsAvailable(filteredOptions);
  }, [options, selectedItems]);

  const handleSelectChange = (selected: InstructorOption | null) => {
    if (selected) {
      // 触发父组件回调
      handleSelect?.(selected);
      // 添加到已选列表
      setSelectedItems(prev => [...prev, selected]);
      // 移除已选的可选选项
      setOptionsAvailable(prev => prev.filter(opt => opt.id !== selected.id));
    }
    setSelectedOption(null);
  };

  const handleOptionRemove = (optionId: string) => {
    // 更新已选列表
    const updatedSelected = selectedItems.filter(item => item.id !== optionId);
    setSelectedItems(updatedSelected);
    // 恢复可选选项
    const restoredOption = options.find(opt => opt.id === optionId);
    if (restoredOption) {
      setOptionsAvailable(prev => {
        const newOptions = [...prev, restoredOption];
        // 字符串id用localeCompare排序,避免数字id异常
        newOptions.sort((a, b) => a.id.localeCompare(b.id));
        return newOptions;
      });
    }
  };

  return (
    <>
      {text && (
        <label className="mt-3 mb-3 block text-black dark:text-white">
          {text}
        </label>
      )}
      <Select
        value={selectedOption}
        onChange={handleSelectChange}
        options={optionsAvailable}
        isSearchable={true}
        placeholder="请选择..."
        // 明确指定react-select的显示文本和值字段
        getOptionLabel={(opt) => opt.label}
        getOptionValue={(opt) => opt.id}
      />
      <br />
      {selectedItems.map((data) => (
        <div className="flex mt-3 flex-row gap-2" key={data.id}>
          <div className="bg-primary text-white rounded-md px-2 py-1">
            {data.value}
          </div>
          <button
            onClick={() => handleOptionRemove(data.id)}
            className="bg-[#EF4444] text-white rounded-md px-2 py-1"
            aria-label="移除选项"
          >
            <BsTrash3 />
          </button>
        </div>
      ))}
    </>
  );
}

export default InputDropDown;

额外优化说明

  • 添加getOptionLabel和getOptionValue属性,确保react-select正确识别选项的显示文本和值字段。
  • 使用localeCompare处理字符串类型id的排序,避免数字id排序异常。
  • 增加类型定义,提升代码可读性和类型安全性。
  • 处理text props为空的情况,避免渲染空标签。

效果验证

修改后,API获取的讲师选项会正常显示在下拉框中,选择后会添加到已选标签列表,移除标签后选项会重新回到下拉框,同时父组件的handleSelect能正常接收选择事件。

内容的提问来源于stack exchange,提问作者Beverly Vladislav

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 16:42:36