Search code examples
reactjstypescriptdrop-down-menu

React select box function


I am trying to make a custom select box component with parent and shild components, with autocomplete and also fetching from api. The problem is that i am trying to fire onchange function from parent to child to select an item from the select box but it is not working, can someone tell me where is the problem?

export function SelectComponent() {
  const [results, setResults] = useState([]);
  const [selectedValue, setSelectedValue] = useState<ComboBoxOption>();

  const handleOnChange = (e: any) => {
    if (!e.target.value.trim()) return setResults([]);

    const filteredValue = results.filter((item: any) =>
      item.value.toString().toLowerCase().startsWith(item.toLowerCase())
    );
    setResults(filteredValue);
  };

  useEffect(() => {
    const fetchData = async () => {
      const response = await axios.get(...);
setResults(response.data);
    };
    fetchData();
  }, []);
  return (
    <div>
      <SelectField
        options={results}
        value={selectedValue?.value}
        onChange={handleOnChange}
        onSelect={item => setSelectedValue(item)}
      />
    </div>
  );
}
export function SelectField({
...
}: SelectFieldProps) {
  const [isOpen, setIsOpen] = useState(false);
  const [isActive, setIsActive] = useState(false);
  const [defaultValue, setDefaultValue] = useState("");

  const handleOnChange: React.ChangeEventHandler<HTMLInputElement> = event => {
    setIsOpen(true);
    setDefaultValue(event.target.value);
    onChange && onChange(event);
  };

  return (
    <div>
      <input
        placeholder={placeholder}
        value={defaultValue}
        onChange={handleOnChange}
      />
      <button onClick={() => {setIsOpen(!isOpen);}}></button>
          <ul>
            {options.map((option: any, index: any) => {
              return (
                <li
                  key={index}
                  onClick={() => {setIsOpen(false);}
                >
                  <span>{option.value}</span>
                </li>
              );
            })}
          </ul>
        )
    </div>
  );
}

Solution

  • It looks like the problem may be in the handleOnChange function in the SelectComponent. The function is trying to filter the results state based on the value of the input element, but it should be filtering based on the e.target.value instead. Also, it's using item.toLowerCase() which doesn't make sense, instead it should use e.target.value.toLowerCase():

    const handleOnChange = (e: any) => {
        if (!e.target.value.trim()) return setResults([]);
    
        const filteredValue = results.filter((item: any) =>
          item.value.toString().toLowerCase().startsWith(e.target.value.toLowerCase())
        );
        setResults(filteredValue);
    };
    

    Also, in the SelectField component, it seems that you are not calling the onSelect prop when an option is selected. You should call the onSelect prop and pass the selected option as a parameter when an option is clicked, like so:

    <li
      key={index}
      onClick={() => {
        setIsOpen(false);
        onSelect(option);
      }}
    >
      <span>{option.value}</span>
    </li>
    

    I would also recommend using onBlur instead of onClick for the input field, this way it can be closed when the user clicks outside of the component.