Skip to content

Instantly share code, notes, and snippets.

@juanpablocs
Last active April 8, 2024 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juanpablocs/565a07b531cb2b12ac19ea2dea57255a to your computer and use it in GitHub Desktop.
Save juanpablocs/565a07b531cb2b12ac19ea2dea57255a to your computer and use it in GitHub Desktop.

SelectBox Typescript

version 1

import React, { FC } from 'react';

// Definición de tipos
interface Option {
  label: string;
  value: string;
}

interface SelectBoxBaseProps {
  options: Option[];
  // ...
}

// Extiende SelectBoxBaseProps para selección múltiple
interface SelectBoxPropsMulti extends SelectBoxBaseProps {
  value: string[];
  onChange: (value: Option[]) => void;
  isMulti: true;
}

// Extiende SelectBoxBaseProps para selección simple
interface SelectBoxPropsSingle extends SelectBoxBaseProps {
  value: string;
  onChange: (value: Option) => void;
  isMulti?: false;
}

// Combinación de ambos tipos de props utilizando la unión de tipos
type SelectBoxProps = SelectBoxPropsMulti | SelectBoxPropsSingle;

const SelectBox: FC<SelectBoxProps> = ({ options, value, onChange, isMulti }) => {
  const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    if (isMulti) {
      const selectedOptions = Array.from(event.target.selectedOptions, option => ({
        value: option.value,
        label: option.label,
      }));
      onChange(selectedOptions);
    } else {
      const selectedOption = {
        value: event.target.value,
        label: event.target.options[event.target.selectedIndex].label,
      };
      onChange(selectedOption);
    }
  };

  return (
    <select multiple={isMulti} value={isMulti ? value : [value]} onChange={handleChange}>
      {options.map(option => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>
  );
};

Version 2

import React from 'react';

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

type OnChangeValue<Option, IsMulti extends boolean> = IsMulti extends true
  ? Option[]
  : Option;

type SelectBoxProps<IsMulti extends boolean> = {
  options: Option[];
  value: IsMulti extends true ? string[] : string;
  onChange: (value: OnChangeValue<Option, IsMulti>) => void;
  isMulti: IsMulti;
};

const SelectBox = <IsMulti extends boolean>({ options, value, onChange, isMulti }: SelectBoxProps<IsMulti>) => {
  const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    let selectedOptions;
    if (isMulti) {
      selectedOptions = Array.from(event.target.selectedOptions, ({ value, label }) => ({ value, label }));
    } else {
      const { value, label } = event.target.options[event.target.selectedIndex];
      selectedOptions = { value, label };
    }
    onChange(selectedOptions as OnChangeValue<Option, IsMulti>);
  };

  return (
    <select multiple={isMulti} value={Array.isArray(value) ? value : [value]} onChange={handleChange}>
      {options.map(option => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>
  );
};

export default SelectBox;

usage this component in differents usecases:

import SelectBox from './components/SelectBox'

function App() {

  const options = [
    { value: 'xx', label: 'XX' },
    { value: 'yy', label: 'YY' },
    { value: 'zz', label: 'ZZ' },
  ];
  return (
    <>
      <p>SelectBox isMulti=false</p>
      <SelectBox 
        isMulti={false} 
        options={options} 
        value={''} 
        onChange={(value) =>{
          console.log({ value })
          // ERROR TS
          // value.map((v) => console.log({ v }))
        }} 
      />
      <hr />
      <p>SelectBox isMulti=true</p>
      <SelectBox 
        isMulti={true} 
        options={options} 
        value={['']} 
        onChange={(value) =>{
          console.log({ value })
          // IT WORK
          value.map((v) => console.log({ v }))
        }}
        />
    </>
  )
}

export default App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment