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

React自定义Dropdown组件选中值不显示问题排查求助

自定义Dropdown选中值不显示问题排查与修复

问题描述

开发自定义Dropdown列表时,数据源已填充,但点击选项(如“Los Angeles”)后,选中值无法替换“Location”占位符。组件由离职同事编写,涉及以下文件:

  • drop-down.tsx(Dropdown组件实现)
  • cities.ts(城市数据源)
  • step-1.tsx(组件调用处)

错误分析

1. 类型不匹配导致选中值无法匹配

Radio组件中定义的Option接口要求id为string类型:

interface Option {
  id: string;
  label: string;
}

但cities.ts中的城市id是number类型:

export const cities: Option[] = [
    {id: 1, label: "New York"}, // id为number类型
    // ...其他城市项
];

组件内selectedOption的类型是string,执行options.find((option) => option.id === selectedOption)时,number与string的全等比较(===)返回false,导致找不到对应选项,始终显示占位符。

2. 焦点事件冲突导致选项点击失效

外层触发区的onBlur事件会在点击下拉选项时提前触发,将active设为false,下拉框直接隐藏,可能无法正确触发单选框的onChange事件,进一步影响选中值的设置。

修复方案

方案1:统一ID类型(核心修复)

两种可选方式:

  • 修改数据源:将cities.ts中的id改为字符串类型,与组件接口对齐:
// cities.ts
interface Option {id: string, label: string}

export const cities: Option[] = [
    {id: "1", label: "New York"},
    {id: "2", label: "San Francisco"},
    {id: "3", label: "Los Angeles"},
    {id: "4", label: "Las Vegas"},
    {id: "5", label: "Chicago"},
    {id: "6", label: "Miami"},
];
  • 修改组件类型:让组件兼容string和number类型的id:
// drop-down.tsx
interface Option {
  id: string | number;
  label: string;
}

const [selectedOption, setSelectedOption] = useState<string | number>("");

方案2:修复焦点事件冲突

修改handleBlur方法,延迟关闭下拉框,确保选项点击事件能正常触发:

const handleBlur = () => {
  // 延迟100ms关闭,给选项点击事件留触发时间
  setTimeout(() => setActive(false), 100);
};

完整修复后的drop-down.tsx示例

import React, { useState } from "react";
import Image from "next/image";

interface Option {
  id: string | number;
  label: string;
}

interface RadioProps {
  options: Option[];
  placeholder?: string;
  width?: string;
  height?: string;
}

const Radio: React.FC<RadioProps> = ({ options, placeholder, height, width }) => {
  const [selectedOption, setSelectedOption] = useState<string | number>("");
  const [active, setActive] = useState<boolean>(false);

  const handleFocus = () => {
    setActive(true);
  };

  const handleBlur = () => {
    setTimeout(() => setActive(false), 100);
  };

  const handleSelect = (optionId: string | number) => {
    setSelectedOption(optionId);
    setActive(false);
  };

  return (
    <div className="relative text-[#BBBBEA]">
      <div
        className={`select relative flex ${height} ${width} cursor-pointer flex-row items-center gap-x-[10px] border-[1px] border-[#9F76FF33] py-[15px] pl-[15px] pr-[10px] ${active ? "border-[#9F76FF]" : "border-[#9F76FF33]"
          } rounded-md text-[14px] leading-6 text-[#BBBBEA]`}
        onFocus={handleFocus}
        onBlur={handleBlur}
        tabIndex={0}
      >
        <span>
          {selectedOption
            ? options.find((option) => option.id === selectedOption)?.label
            : placeholder}
        </span>
        <Image
          className={
            "ml-auto h-[24px] w-[24px] transition-transform duration-300"
          }
          src="..."
          alt="Arrow"
          style={{
            transform: active ? "rotate(-180deg)" : "rotate(0deg)",
          }}
          width={24}
          height={24}
          unoptimized={true}
        />
      </div>
      <div
        className={`options top-15 absolute left-0 w-full z-30 mt-2 rounded-md bg-[#1D1D37] transition-opacity duration-300 ${active ? "opacity-100" : "pointer-events-none opacity-0"}`}
        style={{ maxHeight: "500px", overflowY: "auto" }}
      >
        <div className="p-2">
          <div className="flex flex-col">
            {options.map((option) => (
              <div key={option.id} className="py-1">
                <input
                  id={String(option.id)}
                  name="option"
                  type="radio"
                  className="hidden"
                  checked={selectedOption === option.id}
                  onChange={() => handleSelect(option.id)}
                  onFocus={handleFocus}
                />
                <label
                  htmlFor={String(option.id)}
                  className={`block cursor-pointer rounded-md p-2 hover:bg-gray-700 ${selectedOption === option.id ? "bg-gray-700" : ""
                    }`}
                >
                  {option.label}
                </label>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Radio;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 13:01:10