En muchas aplicaciones web, a menudo nos encontramos con formularios en los que seleccionar una opción en un menú desplegable desbloquea un nuevo conjunto de opciones en otro. Estos menúes desplegables interconectados, comúnmente conocidos como menúes desplegables dependientes o en cascada, desempeñan un papel crucial en la creación de una experiencia de rellenado de formularios fluida e intuitiva.
Ya sea seleccionando un país para revelar estados correspondientes o eligiendo una categoría de producto para mostrar artículos específicos, estos menúes desplegables simplifican las elecciones complejas para todos. Para los desarrolladores, implementar menúes desplegables dependientes es un desafío práctico que combina lógica, usabilidad y manejo de datos dinámicos.
En este tutorial, aprenderás cómo implementar este tipo de menú desplegable en tu aplicación React.
Tabla de Contenidos
¿Qué es un Menú Desplegable Dependiente?
Un menú desplegable dependiente es un elemento de la interfaz de usuario en el que las opciones disponibles en un menú desplegable son determinadas por la selección realizada en otro menú desplegable. Por ejemplo, considera un escenario en el que tienes dos menús desplegables:
-
Menú Desplegable de País: El usuario selecciona un país.
-
Menú Desplegable de Ciudad: Basado en el país seleccionado, la lista de ciudades disponibles en el segundo menú desplegable se filtrará en consecuencia.
Este tipo de interacción es crucial para formularios que requieren entradas de datos complejas y sensibles al contexto.
¿Cómo Funciona un Menú Desplegable Dependiente?
Los menús desplegables dependientes funcionan al actualizar dinámicamente las opciones del segundo menú desplegable basándose en el valor seleccionado en el primer menú desplegable. Este cambio dinámico se logra típicamente mediante:
-
Escuchar la entrada del usuario: Cuando el usuario selecciona una opción en el primer menú desplegable, un evento (generalmente onChange) desencadena una función para actualizar el estado.
- Obteniendo nuevos datos: Este estado actualizado se puede utilizar para filtrar los datos existentes o realizar una llamada a la API para obtener la nueva lista de opciones.
-
Renderizando nuevos datos: Luego, el segundo menú desplegable se actualiza con las nuevas opciones, brindando al usuario opciones relevantes.
Pasos para Crear Menús Desplegables Dependientes en React
Paso 1: Configurar tu Proyecto de React
Si eres nuevo en React y deseas seguir, consulta la documentación de Vite y sigue los pasos para crear tu proyecto de React. Cuando termines, vuelve aquí y continuemos construyendo.
Si ya tienes un proyecto de React que deseas utilizar, ¡también es genial!
Paso 2: Estructurar el Componente
Para simplificar, asumamos que estamos construyendo un menú desplegable dependiente de dos niveles donde el primer menú desplegable te permite elegir un país y el segundo menú desplegable muestra ciudades basadas en el país seleccionado.
Además, en el menú desplegable del país, tendremos otra opción para ingresar un nombre de país que no esté incluido en las opciones de países. El usuario puede entonces proceder a ingresar su país en un campo de texto.
Primero, crea un nuevo archivo llamado DependentDropdown.js
o DependentDropdown.jsx
. Dentro de este archivo, define un componente funcional llamado DependentDropdown
.
Ahora vamos a seguir los siguientes pasos para construir nuestro menú desplegable dependiente:
Declarar Variables para Almacenar Datos
Necesitamos crear datos estáticos para los valores de nuestros países y ciudades:
// Datos estáticos de países
const countries = [
{ id: 1, name: 'USA' },
{ id: 2, name: 'Canada' },
{ id: 3, name: 'Other' },
];
// Datos estáticos de ciudades correspondientes a los países
const cities = {
USA: ['New York', 'Los Angeles', 'Chicago'],
Canada: ['Toronto', 'Vancouver', 'Montreal'],
};
-
countries
es un array de objetos. Cada objeto tiene propiedades deid
yname
. -
cities
es un objeto con nombres de países como claves y los valores como un array de ciudades.
Declarar Variables de Estado
Para cada selección de país o ciudad, queremos poder realizar un seguimiento de los valores seleccionados. También queremos ser capaces de poblar la opción de ciudades después de que se haya realizado una selección de país. Para hacer eso, necesitamos declarar algunos estados.
Si el concepto de estado es nuevo para ti, puedes leer mi artículo sobre el estado aquí.
const [selectedCountry, setSelectedCountry] = useState('');
const [availableCities, setAvailableCities] = useState([]);
const [selectedCity, setSelectedCity] = useState('');
const [otherCountry, setOtherCountry] = useState('');
-
El estado
selectedCountry
está declarado y su valor inicial está configurado como una cadena vacía. -
El estado
availableCities
está declarado y su valor inicial está configurado como un array vacío. -
El estado
selectedCity
está declarado y su valor inicial está configurado como una cadena vacía. -
El estado
otherCountry
está declarado y su valor inicial está configurado como una cadena vacía.
Manejo de Eventos
En el proceso de realizar una selección en el menú desplegable, queremos que se realicen algunas acciones. Los manejadores de eventos nos permiten hacer eso en el caso de un evento, que en este caso es el evento onChange
.
const handleCountryChange = (e) => {
const country = e.target.value;
setSelectedCountry(country);
setAvailableCities(cities[country] || []);
setSelectedCity('');
if (country !== 'Other') {
setOtherCountry('');
}
};
Esto es lo que sucede en la función handleCountryChange
:
-
Obtiene el valor de la opción seleccionada en el menú desplegable (el país que fue seleccionado).
-
La función
setSelectedCountry
actualiza la variable de estado (selectedCountry) con el país recién seleccionado. -
cities[country]
busca la lista de ciudades para el país seleccionado en el objetocities
.-
Si se encuentra
cities[country]
, establece esa lista de ciudades como las ciudades disponibles. -
Si no se encuentran ciudades para el país seleccionado (
cities[country]
es indefinido), el|| []
asegura que se utilice un array vacío ([]
) como alternativa, evitando errores al intentar mostrar las ciudades.
-
-
Cuando el usuario cambia la selección del país, la función
setSelectedCity
restablece la variableselectedCity
a una cadena vacía. -
Si el país seleccionado no es ‘Otro’, el estado
otherCountry
se restablece a una cadena vacía. Esto asegura que si el usuario había escrito algo previamente en el campo “Otro”, ese texto se borre una vez que seleccionen un país diferente (por ejemplo, “EE. UU.” o “Canadá”).
Para la selección del país ‘Otro’, solo necesitamos realizar un seguimiento del valor ingresado en el campo de entrada. La función setOtherCountry
actualiza el valor ingresado. Y así es como se hace:
const handleOtherCountryChange = (e) => {
setOtherCountry(e.target.value);
};
Para el cambio de ciudades, no necesitamos hacer mucho porque el país seleccionado determina qué ciudades se muestran. Todo lo que necesitamos hacer es actualizar la variable selectedCity
al valor de la opción seleccionada en el menú desplegable, que es la ciudad seleccionada.
En React, la función actualizadora se encarga de actualizar las variables de estado, por lo que setSelectedCity
maneja esto en este caso.
La función handleCityChange
será:
const handleCityChange = (e) => {
setSelectedCity(e.target.value);
};
Devolver JSX
El componente DependentDropdown
renderiza tres elementos principales: el dropdown de País, el dropdown de Ciudad y la entrada de texto de País.
Un dropdown en HTML es una combinación de los elementos <select>
y <option>
. Para hacer un seguimiento del valor de los elementos, adjuntaremos variables de estado a ellos para poder controlarlos. Hacer esto se llama ‘Controlar Elementos’, mientras que los elementos en sí se denominan ‘Elementos Controlados’ en React.
Para controlar el elemento <select>
de país, le daremos un atributo value
de selectedCountry
y también adjuntaremos la función handleCountryChange
a él.
<label htmlFor="country" className='font-bold'>Select Country: </label>
<select id="country" value={selectedCountry} onChange={handleCountryChange}>
<option value="">Select a country</option>
{countries.map((country) => (
<option key={country.id} value={country.name}>
{country.name}
</option>
))}
</select>
Además,
-
Dentro del
<option>
, recorremos el arraycountries
y creamos dinámicamente un<option>
para cada objeto de país en el array. -
El
nombre
de cada país se muestra como texto de la opción. -
Cada opción tiene su
key
configurada con elid
del país y elvalue
configurado con elnombre
del país. -
La
key
ayuda a React a gestionar eficientemente la lista al volver a renderizar.
El menú desplegable de Ciudades se renderiza condicionalmente en función del país seleccionado. Si se elige la opción de ‘Otro’ país, se muestra un campo de entrada de texto para que el usuario especifique el país. De lo contrario, si se selecciona un país válido, se muestra un menú desplegable de Ciudades con opciones relevantes.
{selectedCountry === 'Other' ? (
<>
<label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
<input
id="other-country"
type="text"
value={otherCountry}
onChange={handleOtherCountryChange}
placeholder="Enter country name"
/>
</>
) : (
selectedCountry && (
<>
<label htmlFor="city" className='font-bold'>Select City: </label>
<select id="city" value={selectedCity} onChange={handleCityChange}>
<option value="">Select a city</option>
{availableCities.map((city, index) => (
<option key={index} value={city}>
{city}
</option>
))}
</select>
</>
)
)
}
Además:
-
Comprobamos si
selectedCountry
es la opción de ‘Otro’ y mostramos un campo de entrada de texto. -
El campo de entrada de texto tiene un estado de
otherCountry
y la función manejadorahandleOtherCountryChange
adjunta a él. -
Controlamos el elemento
<select>
de la ciudad utilizando el atributovalue
, estableciéndolo en la variable de estado deselectedCity
. La función manejadora,handleCityChange
, también está adjunta para manejar los eventosonChange
. -
Mapeamos sobre el array
availableCities
y creamos dinámicamente un<option>
para cada ciudad en el array. -
La
key
de cada opción se establece en unindex
y elvalue
se establece en lacity
. -
Cada ciudad se muestra como texto de la opción.
Eso es todo lo que tenemos que hacer para tener un menú desplegable dependiente funcional utilizando nuestros datos estáticos.
Aquí está todo el código juntado:
import React, { useState } from 'react';
const DependentDropdown = () => {
// Datos estáticos del país
const countries = [
{ id: 1, name: 'USA' },
{ id: 2, name: 'Canada' },
{ id: 3, name: 'Other' },
];
// Datos estáticos de la ciudad correspondientes a los países
const cities = {
USA: ['New York', 'Los Angeles', 'Chicago'],
Canada: ['Toronto', 'Vancouver', 'Montreal'],
};
// Estado para contener el país seleccionado, la ciudad y otro texto del país
const [selectedCountry, setSelectedCountry] = useState('');
const [availableCities, setAvailableCities] = useState([]);
const [selectedCity, setSelectedCity] = useState('');
const [otherCountry, setOtherCountry] = useState('');
// Manejar el cambio de país
const handleCountryChange = (e) => {
const country = e.target.value;
setSelectedCountry(country);
setAvailableCities(cities[country] || []);
setSelectedCity('');
if (country !== 'Other') {
setOtherCountry('');
}
};
// Manejar el cambio de ciudad
const handleCityChange = (e) => {
setSelectedCity(e.target.value);
};
// Manejar el cambio de entrada de otro país
const handleOtherCountryChange = (e) => {
setOtherCountry(e.target.value);
};
return (
<div className='text-center text-3xl'>
<h1 className='font-extrabold text-5xl p-10'>Dependent Dropdown Example</h1>
{/* Menú desplegable de país */}
<label htmlFor="country" className='font-bold'>Select Country: </label>
<select id="country" value={selectedCountry} onChange={handleCountryChange}>
<option value="">Select a country</option>
{countries.map((country) => (
<option key={country.id} value={country.name}>
{country.name}
</option>
))}
</select>
{/* Ciudad u otra entrada de país */}
{selectedCountry === 'Other' ? (
<>
<label htmlFor="other-country" className='font-bold'>Please specify the country: </label>
<input
id="other-country"
type="text"
value={otherCountry}
onChange={handleOtherCountryChange}
placeholder="Enter country name"
/>
</>
) : (
selectedCountry && (
<>
<label htmlFor="city" className='font-bold'>Select City: </label>
<select id="city" value={selectedCity} onChange={handleCityChange}>
<option value="">Select a city</option>
{availableCities.map((city, index) => (
<option key={index} value={city}>
{city}
</option>
))}
</select>
</>
)
)}
</div>
);
};
export default DependentDropdown;
Paso 3: Usar el componente
Para obtener tus resultados finales, necesitas importar el componente DependentDropdown
a tu App.js
o App.jsx
y colocarlo dentro de la sección de retorno del componente App.
import DependentDropdown from './DependentDropdown'
function App() {
return (
<DependentDropdown/>
)
}
export default App
No olvides ejecutar la aplicación ingresando alguno de estos comandos:
npm start // para crear la aplicación react
npm run dev // para la aplicación react vite
Finalmente, esto es lo que debería renderizarse en tu navegador:
Manejo de datos dinámicos (solicitudes de API)
En aplicaciones del mundo real, las listas de los menús desplegables pueden no ser estáticas. En su lugar, podrían ser obtenidas de una API o de un archivo JSON que actúe como una API.
En este ejemplo, estaremos leyendo datos de un archivo JSON para poblar nuestro menú desplegable dependiente. Esta práctica tiene algunos beneficios que son:
-
Reducción de la carga de la base de datos: Al utilizar un archivo JSON estático (o un archivo pre-cargado), se reduce la cantidad de consultas a la base de datos que de lo contrario serían necesarias para poblar los menús desplegables. Esto es especialmente útil si las opciones del menú desplegable son bastante estáticas y no cambian con frecuencia.
-
Renderizado más rápido de la interfaz de usuario: Dado que los datos ya están en el lado del cliente, no es necesario realizar una solicitud de ida y vuelta al servidor cada vez que el usuario interactúa con el menú desplegable. Esto puede hacer que la interfaz se sienta más receptiva.
Nuestro archivo JSON contiene estados y LGAs (Áreas de Gobierno Local), que son equivalentes a Países y Ciudades.
Los datos en el archivo JSON están representados como una matriz de objetos, y cada objeto tiene claves para estado, alias y lgas. La clave ‘lgas’ contiene una matriz.
Así es como está representado:
[
{
"state": "Adamawa",
"alias": "adamawa",
"lgas": [
"Demsa",
"Fufure",
"Toungo",
"Yola North",
"Yola South"
]
},
{
"state": "Akwa Ibom",
"alias": "akwa_ibom",
"lgas": [
"Abak",
"Uruan",
"Urue-Offong/Oruko",
"Uyo"
]
},
//el resto de los objetos
]
Este método para crear un menú desplegable dependiente dinámico desde una API no es muy diferente del ejemplo anterior, excepto por algunas modificaciones menores.
Aquí está cómo obtuvimos y usamos datos de un archivo JSON:
import React, { useEffect, useState } from "react";
function DependentDropdown() {
//declarando variables de estado globales
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
//obteniendo datos usando el gancho useEffect
useEffect(() => {
fetch("nigeria-state-and-lgas.json") //archivo JSON establecido como URL
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
console.error("Error fetching data:", error);
setLoading(false);
});
}, []);
return loading ? <div>Loading...</div> : <Form data={data} />;
}
//formulario recibiendo datos como props
function Form({ data }) {
//declarando variables de estado locales
const [selectedState, setSelectedState] = useState("");
const [selectedLga, setSelectedLga] = useState("");
const [showList, setShowList] = useState(false);
let sortedData = data.slice().sort((a, b) => a.state.localeCompare(b.state));
const selectedData = sortedData.find((item) => item.state === selectedState);
//función de control para el estado
function handleClickState(e) {
setSelectedState(e.target.value);
setShowList(true);
}
//función de control para LGA
function handleClickLga(e) {
setSelectedLga(e.target.value);
}
return (
<div>
<form onSubmit={handleFormSubmit}>
<div>
{/* Nombre */}
<div>
<label htmlFor="firstName">First Name</label>
<input type="text"
id="firstName"
name="firstName"
placeholder="Enter your first name"/>
</div>
{/* Apellido */}
<div>
<label htmlFor="lastName">
Last Name
</label>
<input
type="text"
id="lastName"
name="lastName"
placeholder="Enter your last name"/>
</div>
</div>
<div>
<div>
<select value={selectedState} onChange={handleClickState} name="state">
<option value="" disabled>Choose your state</option>
{sortedData.map((data) => (
<option key={data.alias} value={data.state}>
{data.state}
</option>
))}
</select>
</div>
{selectedData && showList && (
<select value={selectedLga} onChange={handleClickLga} name="lga">
<option value="" disabled>{`Choose your LGA in ${selectedState}`}</option>
{selectedData.lgas.map((lgass) => (
<option key={lgass} value={lgass}>
{lgass}
</option>
))}
</select>
)}
</div>
<div>
<button type="submit">
Submit
</button>
</div>
</form>
</div>
);
}
export default DependentDropdown;
La modificación clave aquí es la obtención de datos usando el gancho useEffect
, que obtiene los datos de estados y LGA solo en la representación inicial
Así es como se ve esto en el navegador:
Conclusión
En este tutorial, has aprendido cómo crear menús desplegables dependientes en React usando datos estáticos y dinámicos. Ahora puedes usar este tipo de menú desplegable en tus aplicaciones de React.
Si encontraste útil este artículo, puedes conectarte conmigo en LinkedIn para más artículos y publicaciones relacionadas con la programación.
¡Nos vemos en el próximo!
Source:
https://www.freecodecamp.org/news/how-to-build-dependent-dropdowns-in-react/