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;
问题原因与修复方案
核心问题点
- 类型定义错误:
optionsAvailable和option被错误定义为string[]类型,但实际传入的是对象数组,导致初始值解析异常。 - 未同步props更新:仅用
useState初始化无法监听父组件传入的options变化(比如API请求完成后的新数据)。 - 对象比较逻辑失效:直接用
option !== selectedOption比较对象,因对象引用不同导致过滤逻辑失效。 - Props名称不匹配:父组件传入
handleSelect,但子组件接收的是onSelect,导致回调无法触发。 - 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排序异常。 - 增加类型定义,提升代码可读性和类型安全性。
- 处理
textprops为空的情况,避免渲染空标签。
效果验证
修改后,API获取的讲师选项会正常显示在下拉框中,选择后会添加到已选标签列表,移除标签后选项会重新回到下拉框,同时父组件的handleSelect能正常接收选择事件。
内容的提问来源于stack exchange,提问作者Beverly Vladislav
相关产品推荐
相关产品推荐

