🌈 react-select Cheatsheet Completo 🌈
react-select es un componente select totalmente personalizable y extensible para React. Proporciona funcionalidades avanzadas como búsqueda, selecciones múltiples, carga asíncrona de opciones, creación de nuevas opciones y una estilización flexible, todo ello con un enfoque en la accesibilidad.
1. 🌟 Conceptos Clave
- Componente Controlado: Por defecto, el valor de
react-selectse controla a través de la propvaluey se actualiza a través de la proponChange, lo que lo hace predecible y fácil de integrar con el estado de React. - Formato de Opciones:
react-selectespera que las opciones sean objetos con propiedadesvalue(identificador único) ylabel(texto a mostrar).- Ejemplo:
{ value: 'apple', label: 'Manzana' }
- Ejemplo:
- Estilización Flexible: Permite una personalización profunda a través de la prop
styles(CSS-in-JS),className, o la inyección de componentes personalizados. - Accesibilidad: Construido con un enfoque en la accesibilidad, siguiendo las pautas de WAI-ARIA.
2. 🛠️ Configuración Inicial
- Instalación:
npm install react-select # o yarn add react-select - Importación Básica:
import Select from 'react-select'; ``` * Para funcionalidades avanzadas como Async o Creatable, se importan componentes específicos del mismo paquete.
3. 🚀 Uso Básico
3.1. Selector Estándar (Single Select)
import React, { useState } from 'react';
import Select from 'react-select';
const fruitOptions = [
{ value: 'apple', label: 'Manzana' },
{ value: 'banana', label: 'Plátano' },
{ value: 'orange', label: 'Naranja' },
];
function SingleSelectExample() {
const [selectedFruit, setSelectedFruit] = useState(null); // El estado guarda el objeto completo de la opción
const handleChange = (selectedOption) => {
setSelectedFruit(selectedOption);
console.log(`Opción seleccionada:`, selectedOption);
};
return (
<div>
<h3>Selector de Frutas</h3>
<Select
options={fruitOptions}
onChange={handleChange}
value={selectedFruit} // El valor del Select es el objeto de la opción
placeholder="Selecciona una fruta..."
isClearable // Permite borrar la selección
isSearchable // Habilita la búsqueda
/>
{selectedFruit && <p>Has seleccionado: {selectedFruit.label}</p>}
</div>
);
}
export default SingleSelectExample;
3.2. Selector Múltiple (Multi Select)
Simplemente añade la prop isMulti. onChange recibirá un array de objetos de opción.
import React, { useState } from 'react';
import Select from 'react-select';
const techOptions = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue.js' },
{ value: 'angular', label: 'Angular' },
{ value: 'node', label: 'Node.js' },
{ value: 'python', label: 'Python' },
];
function MultiSelectExample() {
const [selectedTechs, setSelectedTechs] = useState([]); // El estado guarda un array de objetos de opción
const handleChange = (selectedOptions) => {
setSelectedTechs(selectedOptions || []); // selectedOptions puede ser null al borrar todo
console.log(`Opciones seleccionadas:`, selectedOptions);
};
return (
<div>
<h3>Selector de Tecnologías</h3>
<Select
isMulti // ¡Esta prop lo convierte en multi-select!
options={techOptions}
onChange={handleChange}
value={selectedTechs} // El valor es un array de objetos de opción
placeholder="Selecciona tecnologías..."
closeMenuOnSelect={false} // Mantener el menú abierto después de seleccionar en multi
/>
{selectedTechs.length > 0 && (
<p>Has seleccionado: {selectedTechs.map(tech => tech.label).join(', ')}</p>
)}
</div>
);
}
exportquire MultipleSelectExample;
4. ⚙️ Props Comunes
Select es muy configurable a través de sus props.
options:Array<object>- Array de objetos de opciones{ value: string|number, label: string }.value:object | Array<object> | null- El objeto(s) de opción seleccionado(s). Controlado.onChange:(newValue: any, actionMeta: ActionMeta) => void- Función que se llama cuando el valor cambia.newValuees el objeto(s) de opción seleccionado(s).isMulti:boolean-truepara selección múltiple.isClearable:boolean-truepara mostrar un botón de “limpiar”.isDisabled:boolean-truepara deshabilitar el selector.isLoading:boolean-truepara mostrar un indicador de carga.isSearchable:boolean-truepara habilitar la funcionalidad de búsqueda.placeholder:string- Texto que se muestra cuando no hay nada seleccionado.defaultValue:object | Array<object>- El valor inicial (para componente no controlado). No usar junto convalue.menuIsOpen:boolean- Controla si el menú desplegable está abierto o cerrado.name:string- El nombre del input (útil para formularios).id:string- El ID del input.className:string- Clase CSS para el contenedor principal.classNamePrefix:string- Prefijo para las clases generadas internamente porreact-select(ej.react-select-control).components:object- Permite reemplazar componentes internos dereact-select(ej.Control,Option,IndicatorSeparator).formatOptionLabel:(option, { context }) => ReactNode- Función para personalizar el renderizado de cada opción en el menú.noOptionsMessage:(obj: { inputValue: string }) => ReactNode- Mensaje que se muestra cuando no hay opciones disponibles.loadingMessage:() => ReactNode- Mensaje que se muestra cuandoisLoadingestrue.filterOption:(option, rawInput) => boolean- Función para personalizar la lógica de filtrado de la búsqueda.getOptionValue:(option) => string- Función para extraer el valor único de una opción (por defectooption.value).getOptionLabel:(option) => string- Función para extraer la etiqueta a mostrar de una opción (por defectooption.label).
5. 🚀 Funcionalidades Avanzadas
5.1. Selector Asíncrono (AsyncSelect)
Para cargar opciones desde una API o una fuente de datos remota.
import React, { useState } from 'react';
import AsyncSelect from 'react-select/async'; // Importa AsyncSelect
const loadUsers = async (inputValue) => {
// Simula una API call
const response = await new Promise(resolve => setTimeout(() => {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
resolve(users.filter(user => user.name.toLowerCase().includes(inputValue.toLowerCase())));
}, 500));
return response.map(user => ({ value: user.id, label: user.name }));
};
function AsyncSelectExample() {
const [selectedUser, setSelectedUser] = useState(null);
return (
<div>
<h3>Buscador Asíncrono de Usuarios</h3>
<AsyncSelect
cacheOptions // Cachea las opciones cargadas
loadOptions={loadUsers} // Función que carga las opciones
defaultOptions // Carga las opciones por defecto al montar (si no quieres cargar solo al escribir)
onChange={setSelectedUser}
value={selectedUser}
placeholder="Escribe para buscar usuarios..."
/>
</div>
);
}
export default AsyncSelectExample;
5.2. Selector “Creatable” (CreatableSelect)
Permite al usuario seleccionar una opción existente o crear una nueva.
import React, { useState } from 'react';
import CreatableSelect from 'react-select/creatable'; // Importa CreatableSelect
const colorOptions = [
{ value: 'red', label: 'Rojo' },
{ value: 'blue', label: 'Azul' },
{ value: 'green', label: 'Verde' },
];
function CreatableSelectExample() {
const [selectedColor, setSelectedColor] = useState(null);
const handleCreate = (inputValue) => {
// Lógica para añadir la nueva opción a tu fuente de datos
console.log(`Creando nueva opción: ${inputValue}`);
const newOption = { value: inputValue.toLowerCase(), label: inputValue };
// Idealmente, aquí también actualizarías tu lista de 'options'
setSelectedColor(newOption); // Selecciona la nueva opción creada
};
return (
<div>
<h3>Selector de Colores (Creatable)</h3>
<CreatableSelect
isClearable
options={colorOptions}
onChange={setSelectedColor}
onCreateOption={handleCreate} // Función para crear nueva opción
value={selectedColor}
placeholder="Selecciona o crea un color..."
formatCreateLabel={(inputValue) => `Crear "${inputValue}"`} // Personaliza el texto de creación
/>
</div>
);
}
export default CreatableSelectExample;
5.3. Opciones Agrupadas
const groupedOptions = [
{
label: 'Frutas',
options: [
{ value: 'apple', label: 'Manzana' },
{ value: 'banana', label: 'Plátano' },
],
},
{
label: 'Vegetales',
options: [
{ value: 'carrot', label: 'Zanahoria' },
{ value: 'brocoli', label: 'Brócoli' },
],
},
];
// Uso: <Select options={groupedOptions} ... />
6. 💅 Estilización
react-select es altamente estilizable.
6.1. Usando la Prop styles (CSS-in-JS)
Permite sobrescribir los estilos de los componentes internos.
const customStyles = {
control: (baseStyles, state) => ({ // Estilos para el contenedor principal
...baseStyles,
borderColor: state.isFocused ? 'blue' : '#ccc',
boxShadow: state.isFocused ? '0 0 0 1px blue' : 'none',
'&:hover': {
borderColor: 'blue',
},
borderRadius: '8px',
minHeight: '40px',
}),
option: (baseStyles, state) => ({ // Estilos para cada opción del menú
...baseStyles,
backgroundColor: state.isFocused ? '#e0f2f7' : (state.isSelected ? '#b2e2f2' : 'white'),
color: state.isSelected ? 'black' : 'inherit',
cursor: 'pointer',
'&:active': {
backgroundColor: '#a0daed',
},
padding: '10px 12px',
}),
singleValue: (baseStyles) => ({ // Estilos para el valor seleccionado en un single select
...baseStyles,
color: '#333',
fontWeight: 'bold',
}),
multiValue: (baseStyles) => ({ // Estilos para cada 'tag' en un multi-select
...baseStyles,
backgroundColor: '#e0e0e0',
borderRadius: '4px',
}),
multiValueLabel: (baseStyles) => ({
...baseStyles,
color: '#333',
}),
multiValueRemove: (baseStyles) => ({
...baseStyles,
color: '#999',
'&:hover': {
backgroundColor: '#ff7878',
color: 'white',
},
}),
placeholder: (baseStyles) => ({
...baseStyles,
color: '#aaa',
}),
menu: (baseStyles) => ({ // Estilos para el menú desplegable
...baseStyles,
borderRadius: '8px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.15)',
zIndex: 9999, // Asegura que el menú esté por encima de otros elementos
}),
indicatorSeparator: (baseStyles) => ({ // Ocultar el separador de indicadores
...baseStyles,
display: 'none',
}),
dropdownIndicator: (baseStyles, state) => ({ // Estilos para el icono de flecha
...baseStyles,
color: state.isFocused ? 'blue' : '#ccc',
transition: 'transform 0.2s ease-in-out',
transform: state.menuIsOpen ? 'rotate(180deg)' : null,
}),
};
// Uso: <Select styles={customStyles} ... />
6.2. Usando className y CSS (o Módulos CSS)
Aplica tus propias clases CSS a la envoltura principal.
// MySelect.module.css
.my-select-container .react-select__control {
border: 2px solid purple;
/* ... otros estilos */
}
.my-select-container .react-select__option--is-focused {
background-color: lightgray;
}
// MyComponent.jsx
import styles from './MySelect.module.css';
<Select
className={styles['my-select-container']}
classNamePrefix="react-select" // Para que las clases internas sean react-select__control, etc.
options={...}
// ...
/>
7. ✍️ Integración con Formularios (ej. React Hook Form)
react-select es un componente controlado, por lo que necesita ser envuelto con el Controller de React Hook Form para su correcta integración.
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import Select from 'react-select';
const options = [
{ value: 'apple', label: 'Manzana' },
{ value: 'banana', label: 'Plátano' },
];
function FormWithReactSelect() {
const { control, handleSubmit, formState: { errors } } = useForm({
defaultValues: {
fruit: null, // Valor inicial para el select
}
});
const onSubmit = (data) => {
console.log('Datos del formulario:', data);
// data.fruit contendrá el objeto de la opción seleccionada (o null)
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
Selecciona una fruta:
<Controller
name="fruit" // Nombre del campo en el formulario
control={control} // Instancia de control de React Hook Form
rules={{ required: '¡Este campo es obligatorio!' }} // Reglas de validación
render={({ field }) => ( // 'field' contiene props { onChange, onBlur, value, name, ref }
<Select
{...field} // Pasa todas las props de `field` (onChange, onBlur, value, etc.)
options={options}
isClearable
placeholder="Elige una fruta..."
/>
)}
/>
{errors.fruit && <p style={{ color: 'red' }}>{errors.fruit.message}</p>}
</label>
<br />
<button type="submit">Enviar Formulario</button>
</form>
);
}
export default FormWithReactSelect;
8. 💡 Buenas Prácticas y Consejos
- Usar Componente Controlado: Prefiere gestionar el estado de
react-selectconvalueyonChangeen el estado de tu componente de React. - Estructura de Opciones (
value,label): Asegúrate de que tus opciones siempre sigan la estructura{ value: someUniqueId, label: 'Display Text' }. Si tus datos no tienen estas claves, usagetOptionValueygetOptionLabelpara mapearlos. - Rendimiento en Listas Grandes: Para listas con miles de opciones, considera utilizar
react-windoworeact-virtualizedconreact-selectpara virtualizar las opciones del menú y mejorar el rendimiento de desplazamiento. - Accesibilidad: Aunque
react-selectestá diseñado pensando en la accesibilidad, asegúrate de que tus personalizaciones no la rompan. Prueba con navegación por teclado y lectores de pantalla. isClearableyisSearchable: Habilita estas props por defecto para una mejor experiencia de usuario.- Manejo de Errores Visual: Cuando uses
react-selecten un formulario, asegúrate de mostrar feedback visual claro (ej. borde rojo, mensaje de error) si el campo es inválido. - Estrategia de Estilización: Para estilos simples, la prop
stylesestá bien. Para temas más complejos o cuando ya usas un sistema CSS (ej. Tailwind, CSS Modules), considera usarclassNamePrefixo inyectar componentes personalizados concomponents. - AsyncSelect para APIs: No intentes cargar todas las opciones de una API si son muchas; usa
AsyncSelecty la proploadOptionspara cargar dinámicamente.
Este cheatsheet te proporciona una referencia completa de react-select, cubriendo sus conceptos esenciales, uso básico, funcionalidades avanzadas, estilización y las mejores prácticas para integrarlo en tus aplicaciones React.