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ário contínua e intuitiva.
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 de dados dinâmicos.
Neste tutorial, você aprenderá como implementar esse 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 em que as opções disponíveis em um dropdown são determinadas pela seleção feita em outro dropdown. Por exemplo, considere um cenário onde 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 entradas de dados complexas e sensíveis ao contexto.
Como Funciona um Dropdown Dependente?
Dropdowns dependentes funcionam tendo as opções do segundo dropdown atualizadas dinamicamente com base no valor selecionado no primeiro dropdown. Essa mudança dinâmica é tipicamente alcançada por meio de:
-
Escutando 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 Suspensos 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 uma entrada 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 as seguintes etapas 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 array de objetos. Cada objeto possui propriedades deid
ename
. -
cities
é um objeto com nomes de países como chaves e os valores como array 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 a seleção de um 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.
Tratamento de Eventos
No processo de fazer uma seleção no dropdown, queremos que algumas ações sejam executadas. 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
:
-
Captura o valor da opção selecionada no dropdown (o país que foi selecionado).
-
A
setSelectedCountry
atualiza a variável de estado (selectedCountry) com o novo país selecionado. -
cities[country]
busca a lista de cidades para o 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 uma alternativa, evitando erros ao tentar exibir as cidades.
-
-
Ao usuário alterar 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 do país ‘Outro’, só precisamos manter o controle do 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, pois 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 atualizadora faz a atualização das variáveis de estado, então o setSelectedCity
cuida disso 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 de País, o dropdown de Cidade e o campo de texto de País.
Um dropdown em HTML é uma combinação dos elementos <select>
e <option>
. Para acompanhar o valor dos elementos, vamos anexar variáveis de estado a eles para que possamos controlá-los. Fazer isso é chamado de ‘Controlando Elementos’, enquanto os próprios elementos são referidos como ‘Elementos Controlados’ no React.
Para controlar o elemento <select>
do país, vamos dar 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>
Além disso,
-
Dentro do
<option>
, mapeamos o arraycountries
e criamos dinamicamente um<option>
para cada objeto país no array. -
O
name
de cada país é exibido como o texto da opção. -
Cada
key
de opção é definida como oid
do país e ovalue
é definido como oname
do país. -
A
key
ajuda o React a gerenciar a lista de forma eficiente durante a re-renderização.
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 é anexado para lidar com os eventosonChange
. -
Mapeamos sobre 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 que precisamos fazer para ter um dropdown dependente funcional usando nossos dados estáticos.
Aqui está todo o código reunido:
import React, { useState } from 'react';
const DependentDropdown = () => {
// 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'],
};
// Estado para manter o país selecionado, cidade e outros textos 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 de 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 Entrada de Outro 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 destes comandos:
npm start // para criar um aplicativo react
npm run dev // para um aplicativo react vite
Por fim, é isso que deve ser renderizado no seu navegador:
Manuseando 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 os menus suspensos. Isso é especialmente útil se as opções do menu suspenso forem bastante estáticas e não mudarem com frequência.
-
Renderização mais rápida da IU: Como os dados já estão do 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 o menu suspenso. Isso pode fazer a interface parecer mais responsiva.
Nosso arquivo JSON contém estados e Municípios, que são equivalentes a Países e Cidades.
Os dados no arquivo JSON são representados como uma matriz de objetos, sendo que cada objeto possui chaves para estado, alias e municípios. A chave ‘municípios’ contém uma matriz.
Aqui está como é 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 restante dos objetos
]
Este método de criar um dropdown dinâmico dependente a partir de uma API não é muito diferente do exemplo anterior, exceto por algumas modificações menores.
Aqui está como buscamos e usamos dados de um arquivo JSON:
import React, { useEffect, useState } from "react";
function DependentDropdown() {
//declarando variáveis de estado globais
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 locais
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 para estado
function handleClickState(e) {
setSelectedState(e.target.value);
setShowList(true);
}
//função manipuladora para 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>
{/* Sobrenome */}
<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 principal modificação aqui é a busca de dados usando o useEffect
, que busca os estados e os dados da LGA apenas na renderização inicial
Aqui está como isso é exibido no navegador:
Conclusão
Neste tutorial, você aprendeu como criar dropdowns dependentes em 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 postagens relacionadas à programação.
Até a próxima!
Source:
https://www.freecodecamp.org/news/how-to-build-dependent-dropdowns-in-react/