0

I am building a custom dropdown list. Dataset for dropdown list is populated. But I can not display selected value from the dropdown list. Can someone please tell me where the error/wrong logic is and how I can fix it.

Thanks a bunch!!!

For example, when I clicked on "Los Angeles", the result does not replace "Location" (a placeholder).

P.S: please excuse the name of the component, my old colleague wrote this piece but he left workplace.

drop-down.tsx

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

interface Option {
  id: string;
  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>("");
  const [active, setActive] = useState<boolean>(false);

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

  const handleBlur = () => {
    setActive(false);
  };

  const handleSelect = (optionId: string) => {
    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={option.id}
                  name="option"
                  type="radio"
                  className="hidden"
                  checked={selectedOption === option.id}
                  onChange={() => handleSelect(option.id)}
                  onFocus={handleFocus}
                  onBlur={handleBlur}
                />
                <label
                  htmlFor={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;

cities.ts

interface Option {id: any, 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"},
];

step-1.tsx

...
<div>
    <Radio
        options = {cities}
        placeholder = "Location"
</div>
2
  • can you add a image of your component? how it looks like? It will give better understanding of what you are expecting in terms of showing selected one. Commented Nov 8, 2024 at 5:55
  • I have added a picture, so when I select Los Angeles, the value (Los Angeles) doesn't replace the placeholder (Location) Commented Nov 8, 2024 at 6:27

1 Answer 1

1

The Problem is that your handleSelect Function is taking optionId as string.

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

while your option have id as number.

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"},
];

And You are doing a strict equality check :

<span>
          {selectedOption
            ? options.find((option) => option.id === selectedOption)?.label
            : placeholder}
        </span>

which is bound to fail as string != number

quickest way to solve this would be either change option.id to string before comparison or change selectedOption to number

Sign up to request clarification or add additional context in comments.

1 Comment

The main issue that you were not warned about this problem by typescript itself is due to loose interfaces , in drop-down.tsx , your interface is interface Option { id: string; label: string; } while in cities.ts the same interface is : interface Option { id: any; label: string; }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.