Pero antes de empezar, primero veamos que es react-select
¿Qué es React Select?
React Select es una biblioteca altamente versátil y ampliamente utilizada en el desarrollo de aplicaciones web con React, ademas de tambien poder ser usada por supuesto en frameworks basados en React como seria Nextjs.
React Select facilita la creación de selectores de elementos personalizados y altamente funcionales dentro de una aplicación. A diferencia de los elementos select
nativos de HTML, React Select brinda a los desarrolladores un control total sobre la apariencia y funcionalidad de los componentes de selección, ademas que evita al desarrollador crear este componente desde cero en cada aplicacion.
React Select permite a los desarrolladores crear desplegables de selección de datos con características avanzadas, como búsqueda en tiempo real, filtrado, paginación y capacidad de etiquetado múltiple. Está diseñado para ser altamente personalizable y se integra fácilmente con otros componentes y bibliotecas de React, lo que lo convierte en una opción poderosa para mejorar la experiencia del usuario en aplicaciones web.
Entonces sabiendo que es React Select, vamos a crear un proyecto desde cero. De hecho para este tutorial estare usando Vitejs, como generador de proyectos, aunque esto tambien funciona con Nextjs perfectamente.
Instalación de React Select con Vite.js
Para comenzar a utilizar React Select en tu proyecto Vite.js, sigue estos pasos sencillos:
Primero Crear un proyecto Vite.js: Si aún no tienes un proyecto Vite.js en marcha, puedes crear uno utilizando la línea de comandos. Ejecuta el siguiente comando para generar un nuevo proyecto:
npm init vite@latest my-react-select-app --template react
Esto creará una nueva carpeta llamada my-react-select-app
con una estructura de proyecto de React y configuración de Vite.js.
Recuerda quitar todos los estilos de los archivo
src/App.css
y el archivosrc/index.css
.
Luego sigue con la Instalación de React Select. Una vez que tienes tu proyecto Vite.js configurado, dirígete a la carpeta del proyecto en tu terminal y ejecuta el siguiente comando para instalar React Select y sus dependencias:
npm install react-select
Esto agregará React Select a tu proyecto y lo hará disponible para su uso.
Luego vamos a Importar React Select, Ahora puedes importar React Select en tus componentes de React donde desees utilizarlo. Por ejemplo:
import Select from "react-select";
export default function App() {
return <Select />;
}
¡Listo! Ahora tienes React Select integrado en tu proyecto Vite.js y estás listo para crear selectores de elementos interactivos y personalizados en tus aplicaciones web.
Options de React Select
Creado el componente, ahora vamos a pasarle las opciones a React Select. Para esto podemos crear un arreglo de objetos, en donde cada objeto esta conformado por dos propiedades, una propiedad value
y otra propiedad llamada label
.
import Select from "react-select";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
];
export default function App() {
return <Select options={options} />;
}
onChange
Para capturar la selección de una opción en la lista desplegable, podemos utilizar el evento onChange
de React Select de la siguiente manera:
<Select
options={options}
onChange={(selectedOption) => console.log(selectedOption)}
/>
El evento onChange se activa cuando el usuario elige una opción y proporciona la opción seleccionada como argumento, lo que nos permite realizar acciones específicas en función de la selección del usuario.
Multi Option en React Select
Para poder seleccionar multiples opciones desde el mismo Select basta con añadir la propiedad isMulti
export default function App() {
return <Select isMulti options={options} />;
}
Por defecto, cuando seleccionamos una opción, el Select se cierra automáticamente. Si deseamos evitar este comportamiento y mantener el menú desplegado después de la selección, podemos utilizar la propiedad closeMenuOnSelect y establecerla en false, como se muestra a continuación:
<Select isMulti options={options} closeMenuOnSelect={false} />;
Además, tenemos la opción de asignar valores por defecto a las opciones seleccionadas proporcionando un array de objetos como valor inicial:
<Select
isMulti
options={options}
closeMenuOnSelect={false}
defaultValue={[options[1], options[2]]}
onChange={(selectedOption) => console.log(selectedOption)}
/>
Es importante destacar que al seleccionar múltiples elementos, el evento onChange proporcionará un Array de JavaScript que contiene las opciones seleccionadas en lugar de un solo objeto.
Animated Components
Luego ademas de crear los componentes tambien podemos animarlos usando una característica mas de react-select
:
import Select from "react-select";
import makeAnimated from "react-select/animated";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
];
const animatedComponents = makeAnimated();
export default function App() {
return (
<Select
isMulti
options={options}
components={animatedComponents}
closeMenuOnSelect={false}
defaultValue={[options[1], options[2]]}
/>
);
}
Async Select
Cuando usamos react-select es posible que le pasemos datos que nuestra aplicacion ya posee quizas en un estado o un prop, pero hay veces que necesitamos obtener datos a medida que se necesiten, por ejemplo en un input de busqueda donde hay muchos datos y estos vienen del backend a medida que vamos tipeando.
import AsyncSelect from "react-select/async";
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "cake", label: "Cake" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" },
];
const loadOptions = (
searchValue: string,
callback: (options: { value: string; label: string }[]) => void
) => {
setTimeout(() => {
const filteredOptions = options.filter((option) =>
option.label.toLowerCase().includes(searchValue.toLowerCase())
);
callback(filteredOptions);
}, 2000);
};
export default function App() {
return (
<AsyncSelect
loadOptions={loadOptions}
onChange={(selectedOption) => console.log(selectedOption)}
/>
);
}
O tambien podemos cargar los datos al iniciar el componente y tener toda la lista disponible:
<AsyncSelect
loadOptions={loadOptions}
defaultOptions
onChange={(selectedOption) => console.log(selectedOption)}
/>
Y esto tambien funciona usando isMulti
:
<AsyncSelect
loadOptions={loadOptions}
defaultOptions
onChange={(selectedOption) => console.log(selectedOption)}
isMulti
/>
Estilos en React Select
Para añadir estilos tambien podemos hacerlo usando un prop llamado styles
:
import AsyncSelect from "react-select/async";
import { StylesConfig } from "react-select";
interface Option {
value: string;
label: string;
color: string;
}
const options = [
{ value: "chocolate", label: "Chocolate", color: "brown" },
{ value: "strawberry", label: "Strawberry", color: "red" },
{ value: "vanilla", label: "Vanilla", color: "white" },
{ value: "cake", label: "Cake", color: "yellow" },
];
const loadOptions = (
searchValue: string,
callback: (options: Option[]) => void
) => {
setTimeout(() => {
const filteredOptions = options.filter((option) =>
option.label.toLowerCase().includes(searchValue.toLowerCase())
);
callback(filteredOptions);
}, 2000);
};
const colorStyles: StylesConfig<Option> = {
control: (styles) => ({ ...styles, backgroundColor: "#EEE0CB" }),
option: (styles, { data, isDisabled, isFocused, isSelected }) => {
console.log(styles, data, isDisabled, isFocused, isSelected);
return {
...styles,
background: "#EEE0CB",
color: data.color,
};
},
};
export default function App() {
return (
<AsyncSelect
loadOptions={loadOptions}
defaultOptions
styles={colorStyles}
onChange={(selectedOption) => console.log(selectedOption)}
// isMulti
/>
);
}
Esto es otro ejemplo basado en la documnetacion mas completo en donde tambien añade un circulo dentro del select basado en el color:
import Select, { StylesConfig } from "react-select";
import chroma from "chroma-js";
interface ColourOption {
value: string;
label: string;
color: string;
}
const options = [
{
value: "red",
label: "Red",
color: "#FF0000",
},
{
value: "blue",
label: "Blue",
color: "#0000FF",
},
{
value: "orange",
label: "Orange",
color: "#FFA500",
}
];
const dot = (color = 'transparent') => ({
alignItems: 'center',
display: 'flex',
':before': {
backgroundColor: color,
borderRadius: 10,
content: '" "',
display: 'block',
marginRight: 8,
height: 10,
width: 10,
},
});
const colourStyles: StylesConfig<ColourOption> = {
control: (styles) => ({
...styles,
background: "#EEE0CB",
color: "#fff",
}),
option: (styles, { data, isDisabled, isFocused, isSelected }) => {
const color = chroma(data.color);
return {
...styles,
backgroundColor: isDisabled
? undefined
: isSelected
? data.color
: isFocused
? color.alpha(0.1).css()
: undefined,
color: isDisabled
? "#ccc"
: isSelected
? chroma.contrast(color, "white") > 2
? "white"
: "black"
: data.color,
cursor: isDisabled ? "not-allowed" : "default",
":active": {
...styles[":active"],
backgroundColor: !isDisabled
? isSelected
? data.color
: color.alpha(0.3).css()
: undefined,
},
};
},
input: (styles) => ({ ...styles, ...dot() }),
placeholder: (styles) => ({ ...styles, ...dot('#ccc') }),
singleValue: (styles, { data }) => ({ ...styles, ...dot(data.color) })
};
export default function App() {
return (
<Select options={options} defaultValue={options[3]} styles={colourStyles} />
);
}
Esto tambien funciona usando Opciones Multiples
const colorStyles: StylesConfig<Option> = {
control: (styles) => ({ ...styles, backgroundColor: "#EEE0CB" }),
option: (styles, { data, isDisabled, isFocused, isSelected }) => {
console.log(styles, data, isDisabled, isFocused, isSelected);
return {
...styles,
background: "#EEE0CB",
color: data.color,
};
},
multiValue: (styles, { data }) => {
return {
...styles,
background: data.color,
color: "#fff",
};
},
multiValueLabel: (styles, { data }) => ({
...styles,
color: "#fff",
}),
multiValueRemove: (styles, { data }) => ({
...styles,
color: "#fff",
cursor: "pointer",
":hover": {
background: data.color,
color: "#333",
},
}),
};
export default function App() {
return (
<AsyncSelect
loadOptions={loadOptions}
defaultOptions
styles={colorStyles}
onChange={(selectedOption) => console.log(selectedOption)}
isMulti
/>
);
}
Creatable
import { useState } from "react";
import CreatableSelect from "react-select/creatable";
interface Option {
value: string;
label: string;
color: string;
}
const options = [
{ value: "chocolate", label: "Chocolate", color: "brown" },
{ value: "strawberry", label: "Strawberry", color: "red" },
{ value: "vanilla", label: "Vanilla", color: "white" },
{ value: "cake", label: "Cake", color: "orange" },
];
export default function App() {
const [opts, setOpts] = useState<Option[]>(options);
const handleInputChange = (inputValue: string, actionMeta: any) => {
console.log("Input Changed");
console.log(inputValue);
console.log(actionMeta);
};
const handleCreate = (inputValue: string) => {
console.log("Create Option");
console.log(inputValue);
const newOption = {
value: inputValue.toLowerCase(),
label: inputValue,
color: "purple",
};
setOpts([...opts, newOption]);
};
return (
<CreatableSelect
options={opts}
onChange={(selectedOption) => console.log(selectedOption)}
onCreateOption={handleCreate}
closeMenuOnSelect={false}
/>
);
}