Em muitas aplicações web, frequentemente nos deparamos com formulários nos quais selecionar uma opção em um menu suspenso desbloqueia um novo conjunto de opções em outro. Esses menus suspensos interconectados, comumente conhecidos como menus suspensos dependentes ou em cascata, desempenham um papel crucial na criação de uma experiência de preenchimento de formulários intuitiva e perfeita.
Seja selecionando um país para revelar estados correspondentes ou escolhendo uma categoria de produto para exibir itens específicos, esses menus suspensos simplificam escolhas complexas para todos. Para os desenvolvedores, implementar menus suspensos dependentes é um desafio prático que combina lógica, usabilidade e manipulação dinâmica de dados.
Neste tutorial, você aprenderá como implementar este tipo de menu suspenso em sua aplicação React.
Sumário
O que é um Dropdown Dependente?
Um dropdown dependente é um elemento de interface do usuário no qual as opções disponíveis em um dropdown são determinadas pela seleção feita em outro dropdown. Por exemplo, considere um cenário em que você tem dois dropdowns:
-
Dropdown de País: O usuário seleciona um país.
-
Dropdown de Cidade: Com base no país selecionado, a lista de cidades disponíveis no segundo dropdown será filtrada de acordo.
Esse tipo de interação é crucial para formulários que exigem inputs de dados complexos e sensíveis ao contexto.
Como Funciona um Dropdown Dependente?
Dropdowns dependentes funcionam atualizando dinamicamente as opções do segundo dropdown com base no valor selecionado no primeiro dropdown. Essa mudança dinâmica é tipicamente alcançada por:
-
Ouvir a entrada do usuário: Quando o usuário seleciona uma opção no primeiro dropdown, um evento (geralmente onChange) aciona uma função para atualizar o estado.
-
Buscando novos dados: Este estado atualizado pode ser usado para filtrar os dados existentes ou fazer uma chamada de API para buscar a nova lista de opções.
-
Renderizando novos dados: O segundo menu suspenso é então atualizado com as novas opções, fornecendo ao usuário escolhas relevantes.
Passos para Criar Menus Dependentes no React
Passo 1: Configurar Seu Projeto React
Se você é novo no React e deseja acompanhar, confira a documentação do Vite e siga os passos para criar seu projeto React. Quando terminar, volte aqui e continuaremos a construção.
Se você já tem um projeto React que deseja usar, ótimo também.
Passo 2: Estruturar o Componente
Para simplificar, vamos supor que estamos construindo um menu suspenso dependente de dois níveis, onde o primeiro menu suspenso permite escolher um país e o segundo menu suspenso exibe cidades com base no país selecionado.
Além disso, no menu suspenso do país, teremos outra opção para inserir um nome de país que não está incluído nas opções de países. O usuário pode então prosseguir para inserir seu país em um campo de texto.
Primeiro, crie um novo arquivo chamado DependentDropdown.js
ou DependentDropdown.jsx
. Dentro deste arquivo, defina um componente funcional chamado DependentDropdown
.
Agora vamos seguir os seguintes passos para construir nosso menu suspenso dependente:
Declarar Variáveis para Armazenar Dados
Precisamos criar dados estáticos para os valores de nossos países e cidades:
// Dados estáticos do país
const countries = [
{ id: 1, name: 'USA' },
{ id: 2, name: 'Canada' },
{ id: 3, name: 'Other' },
];
// Dados estáticos da cidade correspondentes aos países
const cities = {
USA: ['New York', 'Los Angeles', 'Chicago'],
Canada: ['Toronto', 'Vancouver', 'Montreal'],
};
-
countries
é uma matriz de objetos. Cada objeto tem propriedades deid
ename
. -
cities
é um objeto com nomes de países como chaves e os valores como matriz de cidades.
Declarar Variáveis de Estado
Para cada seleção de país ou cidades, queremos ser capazes de acompanhar os valores selecionados. Também queremos ser capazes de preencher a opção de cidades após uma seleção de país ter sido feita. Para fazer isso, precisamos declarar alguns estados.
Se o conceito de estado é novo para você, você pode ler meu artigo sobre estado aqui.
const [selectedCountry, setSelectedCountry] = useState('');
const [availableCities, setAvailableCities] = useState([]);
const [selectedCity, setSelectedCity] = useState('');
const [otherCountry, setOtherCountry] = useState('');
-
O estado
selectedCountry
é declarado e seu valor inicial é definido como uma string vazia. -
O estado
availableCities
é declarado e seu valor inicial é definido como um array vazio. -
O estado
selectedCity
é declarado e seu valor inicial é definido como uma string vazia. -
O estado
otherCountry
é declarado e seu valor inicial é definido como uma string vazia.
Manipulação de Eventos
No processo de fazer uma seleção no menu suspenso, queremos que algumas ações sejam realizadas. Os manipuladores de eventos nos permitem fazer isso no caso de um evento, que neste caso é o evento onChange
.
const handleCountryChange = (e) => {
const country = e.target.value;
setSelectedCountry(country);
setAvailableCities(cities[country] || []);
setSelectedCity('');
if (country !== 'Other') {
setOtherCountry('');
}
};
Aqui está o que está acontecendo na função handleCountryChange
:
-
Pega o valor da opção selecionada no menu suspenso (o país que foi selecionado).
-
O
setSelectedCountry
atualiza a variável de estado (selectedCountry) com o país recém-selecionado. -
cities[country]
procura a lista de cidades do país selecionado no objetocities
.-
Se
cities[country]
for encontrado, define essa lista de cidades como as cidades disponíveis. -
Se nenhuma cidade for encontrada para o país selecionado (
cities[country]
é indefinido), o|| []
garante que um array vazio ([]
) seja usado como alternativa, evitando erros ao tentar exibir as cidades.
-
-
Quando o usuário altera a seleção do país, a função
setSelectedCity
redefine oselectedCity
para uma string vazia. -
Se o país selecionado não for ‘Outro’, o estado
otherCountry
é redefinido para uma string vazia. Isso garante que se o usuário tivesse digitado algo anteriormente na entrada “Outro”, esse texto seja apagado assim que selecionarem um país diferente (por exemplo, “EUA” ou “Canadá”).
Para a seleção de país ‘Outro’, precisamos apenas acompanhar o valor inserido na entrada. A função setOtherCountry
atualiza o valor inserido. E é assim que é feito:
const handleOtherCountryChange = (e) => {
setOtherCountry(e.target.value);
};
Para a mudança de cidades, não precisamos fazer muito porque o país selecionado determina quais cidades são exibidas. Tudo o que precisamos fazer é atualizar o selectedCity
para o valor da opção selecionada no menu suspenso, que é a cidade selecionada.
No React, a função de atualização faz a atualização das variáveis de estado, então o setSelectedCity
lida com isso neste caso.
A função handleCityChange
será:
const handleCityChange = (e) => {
setSelectedCity(e.target.value);
};
Retornando JSX
O componente DependentDropdown
renderiza três elementos principais: o dropdown do País, o dropdown da Cidade, e a entrada de texto do País.
Um dropdown em HTML é uma combinação dos elementos <select>
e <option>
. Para acompanhar o valor dos elementos, vamos atribuir variáveis de estado a eles para que possamos controlá-los. Fazer isso é chamado de ‘Controlar Elementos’, enquanto os elementos em si são referidos como ‘Elementos Controlados’ no React.
Para controlar o elemento <select>
do país, vamos atribuir a ele um atributo value
de selectedCountry
e também anexar a função handleCountryChange
a ele.
<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>
Também,
-
Dentro do
<option>
, mapeamos sobre o arraycountries
e criamos dinamicamente um<option>
para cada objeto de país no array. -
O
name
de cada país é exibido como texto da opção. -
Cada chave de opção é definida como o
id
do país e ovalor
é definido como onome
do país. -
A
chave
ajuda o React a gerenciar a lista de forma eficiente ao recriar.
O menu suspenso de Cidades é renderizado condicionalmente com base no país selecionado. Se a opção de país ‘Outro’ for escolhida, um campo de entrada de texto é exibido para o usuário especificar o país. Caso contrário, se um país válido for selecionado, um menu suspenso de Cidades com opções relevantes é mostrado.
{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>
</>
)
)
}
Além disso:
-
Verificamos se
selectedCountry
é a opção ‘Outro’ e exibimos um campo de entrada de texto. -
O campo de entrada de texto tem um estado
otherCountry
e a função manipuladorahandleOtherCountryChange
anexada a ele. -
Controlamos o elemento
<select>
da cidade usando o atributovalue
, definindo-o como a variável de estadoselectedCity
. O manipulador de eventos,handleCityChange
, também está anexado para lidar com eventosonChange
. -
Mapeamos o array
availableCities
e criamos dinamicamente um<option>
para cada cidade no array. -
O
key
de cada opção é definido como umindex
e ovalue
é definido como acity
. -
Cada cidade é exibida como texto da opção.
Isso é tudo o que precisamos fazer para ter um menu suspenso dependente funcional usando nossos dados estáticos.
Aqui está todo o código reunido:
import React, { useState } from 'react';
const DependentDropdown = () => {
// Dados de país estáticos
const countries = [
{ id: 1, name: 'USA' },
{ id: 2, name: 'Canada' },
{ id: 3, name: 'Other' },
];
// Dados de cidade estáticos correspondentes aos países
const cities = {
USA: ['New York', 'Los Angeles', 'Chicago'],
Canada: ['Toronto', 'Vancouver', 'Montreal'],
};
// Estado para manter o país selecionado, cidade e outro texto de país
const [selectedCountry, setSelectedCountry] = useState('');
const [availableCities, setAvailableCities] = useState([]);
const [selectedCity, setSelectedCity] = useState('');
const [otherCountry, setOtherCountry] = useState('');
// Lidar com a mudança de país
const handleCountryChange = (e) => {
const country = e.target.value;
setSelectedCountry(country);
setAvailableCities(cities[country] || []);
setSelectedCity('');
if (country !== 'Other') {
setOtherCountry('');
}
};
// Lidar com a mudança de cidade
const handleCityChange = (e) => {
setSelectedCity(e.target.value);
};
// Lidar com a mudança na entrada de outro 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>
{/* Dropdown 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>
{/* Cidade ou Outra 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;
Passo 3: Usar o Componente
Para obter seus resultados finais, você precisa importar o componente DependentDropdown
no seu App.js
ou App.jsx
e colocá-lo dentro da seção de retorno do componente App.
import DependentDropdown from './DependentDropdown'
function App() {
return (
<DependentDropdown/>
)
}
export default App
Não se esqueça de executar a aplicação digitando um desses comandos:
npm start // para criar um aplicativo react
npm run dev // para aplicativo react vite
Por fim, isso é o que deve ser renderizado em seu navegador:
Manipulação de Dados Dinâmicos (Requisições de API)
Nas aplicações do mundo real, as listas para os dropdowns podem não ser estáticas. Em vez disso, elas podem ser obtidas de uma API ou de um arquivo JSON atuando como uma API.
Neste exemplo, estaremos lendo dados de um arquivo JSON para popular nosso dropdown dependente. Essa prática tem alguns benefícios que são:
-
Redução da carga do banco de dados: Ao usar um arquivo JSON estático (ou um arquivo pré-carregado), você está reduzindo o número de consultas ao banco de dados que seriam necessárias para popular as listas suspensas. Isso é especialmente útil se as opções da lista suspensa forem bastante estáticas e raramente mudarem.
-
Renderização mais rápida da interface do usuário: Como os dados já estão no lado do cliente, não há necessidade de uma solicitação de ida e volta para o servidor toda vez que o usuário interage com a lista suspensa. Isso pode fazer com que a interface pareça mais responsiva.
Nosso arquivo JSON contém estados e Áreas de Governo Local (Local Government Areas), que são equivalentes a Países e Cidades.
Os dados no arquivo JSON são representados como um array de objetos, sendo que cada objeto possui chaves para estado, alias e lgas. A chave ‘lgas’ contém um array.
Aqui está 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"
]
},
//o resto dos objetos
]
Este método de criação de um dropdown dinâmico dependente de uma API não é muito diferente do exemplo anterior, exceto por algumas modificações pequenas.
Aqui está como buscamos e usamos dados de um arquivo JSON:
import React, { useEffect, useState } from "react";
function DependentDropdown() {
//declarando variáveis de estado global
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
//buscando dados usando o hook useEffect
useEffect(() => {
fetch("nigeria-state-and-lgas.json") //arquivo JSON definido 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} />;
}
//formulário recebendo dados como props
function Form({ data }) {
//declarando variáveis de estado local
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);
//função manipuladora de estado
function handleClickState(e) {
setSelectedState(e.target.value);
setShowList(true);
}
//função manipuladora de LGA
function handleClickLga(e) {
setSelectedLga(e.target.value);
}
return (
<div>
<form onSubmit={handleFormSubmit}>
<div>
{/* Primeiro Nome */}
<div>
<label htmlFor="firstName">First Name</label>
<input type="text"
id="firstName"
name="firstName"
placeholder="Enter your first name"/>
</div>
{/* Último Nome */}
<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;
A modificação chave aqui é a busca de dados usando o hook useEffect
, que busca os dados de estados e LGA apenas na renderização inicial
Aqui está como isso é renderizado no navegador:
Conclusão
Neste tutorial, você aprendeu como criar dropdowns dependentes no React usando dados estáticos e dinâmicos. Agora você pode usar esse tipo de dropdown em suas aplicações React.
Se você achou este artigo útil, pode se conectar comigo no LinkedIn para mais artigos e posts relacionados à programação.
Até a próxima!
Source:
https://www.freecodecamp.org/news/how-to-build-dependent-dropdowns-in-react/