Conditionally display input field - reactjs

I am working on form in react. After selecting dish type I want to conditionally display other fields.
For example if I select pizza I want to display number field. If I select soup I want to display other input field.
Here is sample of code:
const Form = () => {
const [dishes] = React.useState([
{
label: "Pizza",
value: "Pizza",
},
{ label: "Soup", value: "Soup" },
{ label: "Sandwich", value: "Sandwich" },
]);
return (
<div>
<form>
<label>Name</label>
<input type="text" required></input>
<label>Preperation Time</label>
<input type="time" step="2" required></input>
<label>Type</label>
<select>
{dishes.map((dish) => (
<option key={dish.value} value={dish.value}>
{dish.label}
</option>
))}
</select>
<button>submit</button>
</form>
</div>
);
};
export default Form;

import React, { useState } from 'react';
function App() {
const [dishes] = React.useState([
{
label: "Pizza",
value: "Pizza",
},
{ label: "Soup", value: "Soup" },
{ label: "Sandwich", value: "Sandwich" },
]);
const [type, setType] = useState([])
const handleChang = (value) => {
setType(value);
}
return (
<div>
<form>
{(type == "Pizza") && (<> <label>Name</label>
<input type="text" required></input></>)
}
{(type == "Soup") && (<> <label>Preperation Time</label>
<input type="time" step="2" required></input></>)
}
<label>Type</label>
<select onChange={(e) => handleChang(e.target.value)}>
{dishes.map((dish) => (
<option key={dish.value} value={dish.value}>
{dish.label}
</option>
))}
</select>
<button>submit</button>
</form>
</div>
);
}
export default App;

Try this (codesandbox: https://codesandbox.io/s/vigilant-kepler-5xuio?file=/src/App.js)
const [dishes] = React.useState([
{
label: "Pizza",
value: "Pizza",
field: (
<div>
<label htmlFor="pizza">Toppings</label>
<input type="number" id="pizza" />
</div>
)
},
{
label: "Soup",
value: "Soup",
field: (
<div>
<label htmlFor="soup">How soupy?</label>
<input type="range" id="soup" />
</div>
)
},
{
label: "Sandwich",
value: "Sandwich",
field: (
<div>
<label htmlFor="sandwich">Enter your ingredients</label>
<input type="text" id="sandwich" />
</div>
)
}
]);
const [selectedDish, setSelectedDish] = React.useState(dishes[0]);
const handleDishSelect = (e) => {
const dish = dishes.find((dish) => dish.value === e.target.value);
if (dish) {
setSelectedDish(dish);
}
};
return (
<div>
<form>
<label>Name</label>
<input type="text" required></input>
<label>Preperation Time</label>
<input type="time" step="2" required></input>
<label>Type</label>
<select onChange={handleDishSelect}>
{dishes.map((dish) => (
<option key={dish.value} value={dish.value}>
{dish.label}
</option>
))}
</select>
{selectedDish && selectedDish.field}
<button>submit</button>
</form>
</div>
);

So you wanna display some JSX on condition right?
you can just check the condition and select a output as if condition
<div>
{dishes.value.equals("pizza") && (<input type="number">)}
{dishes.value.equals("soup") && (<input type="text">)}
</div>
You can use Ternary operation to if else condition
<div>
{dishes.value.equals("pizza") ? (<input type="number">) : (<input type="text">)}
</div>

This is the architecture I would follow (see below). First define your menu, which I presume is not changing so should just be a plain object. Then split you code in a few components:
MenuForm - this can be your main menu form component;
MenuItem - this is a component that generates selection options for a menu item
Crete additional components to handle the specifics of each menu item, say PizzaItemMenu, SoupItemMenu, etc.
Here is a full example app in action based on your code:
const menu = {
pizza: {
type: [
{
label: 'neapolitana',
value: 'npa'
}, {
label: 'chicago',
value: 'ccg'
}, {
label: '4-cheese',
value: 'cse'
}],
number: 1,
addOn: false
},
soup: {
type: [{
label: 'carrot',
value: 'crt'
}, {
label: 'potato',
value: 'ptt'
}],
number: 1,
addOn: false
}
}
const MenuForm = () => {
const [menuItem, setMenuItem] = React.useState(false);
const handleMenuItemSelection = (e) => {
setMenuItem(e.target.value);
}
const additionalMenuItemSelections = () => {
switch (menuItem) {
case 'pizza':
return <PizzaItemMenu />
case 'soup':
return <SoupItemMenu />
default:
return false;
}
}
return (
<div>
<form>
<label>Name</label>
<input type="text" required></input>
<label>Preperation Time</label>
<input type="time" step="2" required></input>
<label>Type</label>
<select className='menu' onChange={handleMenuItemSelection} defaultValue='x'>
<option disabled value="x">Select menu item</option>
{Object.keys(menu).map((item, index) => {
return (
<option key={index} value={item}>{item}</option>
)
})}
</select>
{additionalMenuItemSelections()}
<button>submit</button>
</form>
</div>
);
};
const PizzaItemMenu = () => {
const [pizzaType, setPizzaType] = React.useState(false);
const handleSelection = (e) => {
const data = e.target.value;
setPizzaType(data);
}
return (
<div className='soup-menu'>
<p>You've selected pizza!</p>
<MenuItem menuItem='pizza' handleSelection={handleSelection} />
Additional form menu selection options for pizzas goes here
{pizzaType && <p>You've selected {pizzaType} pizza!</p>}
</div>
)
}
const SoupItemMenu = () => {
const [soupType, setSoupType] = React.useState(false);
const handleSelection = (e) => {
const data = e.target.value;
setSoupType(data);
}
return (
<div className='soup-menu'>
<p>You've selected soup!</p>
<MenuItem menuItem='soup' handleSelection={handleSelection} />
Additional form menu selection options for soups goes here
{soupType && (<p>You've selected {soupType} soup!</p>)}
</div>
)
}
const MenuItem = ({ menuItem, handleSelection }) => {
const itemOptions = menu[menuItem].type;
return (
<select id={menuItem} defaultValue='x' onChange={(e) => handleSelection(e)} >
<option disabled value="x">Select {menuItem} type</option>
{itemOptions.map((itemKey, index) => (
<option key={index} value={itemKey.value}>{itemKey.label}</option>
))}
</select>
)
}
export default MenuForm;

Related

Im a React Beginner and need to know how to Lift up State from my form

So ive got a Booking component which returns a form which lets you choose number of Guests, Time, Occasion and a Date, but i need a way to lift the State up from this component so that i can use it in the future with an API.
I think i dont have the knowledge of the right Syntax or correct way of putting this component and its state. Ive been trying to Code it but the only thing i came up with is this:
import React from 'react'
import { useState } from 'react';
const availableTimes = [
{value: "17:00", text: "17:00"},
{value: "18:00", text: "18:00"},
{value: "19:00", text: "19:00"},
{value: "20:00", text: "20:00"},
{value: "21:00", text: "21:00"},
{value: "22:00", text: "22:00"},
]
const Guests = [
{value: "1", text: "1"},
{value: "2", text: "2"},
{value: "3", text: "3"},
{value: "4", text: "4"},
{value: "5", text: "5"},
{value: "6", text: "6"},
{value: "7", text: "7"},
{value: "8", text: "8"},
{value: "9", text: "9"},
]
const Occasion = [
{value: "Birthday", text: "Birthday"},
{value: "Anniversary", text: "Anniversary"},
]
const Select = ({value, options, onChange}) => {
return(
<select value={value} onChange={onChange}>
{options.map(option => {
return(<option key={option.value} value={option.value}>{option.text}</option>)
})}
</select>
)
}
const Bookingform = () => {
const[resdate, setResdate] = useState("");
const[guests, setGuests] = useState("");
const[occasion, setOccasion] =useState("");
const[restime, setRestime] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const data = {
Reservation_Time: restime,
Occasion: occasion,
Guests: guests,
Reservation_Date: resdate
}
const json = JSON.stringify(data);
console.clear()
console.log(json)
setResdate("");
setRestime("");
setGuests("");
setOccasion("");
console.log("Form submitted!");
}
return (
<>
<div className='BookingForm'>
<form onSubmit={handleSubmit}>
<fieldset>
<div className='Field'>
<field>
<label htmlFor='res-date'>Choose Date</label>
<input id="res-date" type="date" placeholder='res-date' name='res-date' value={resdate} onChange={(e)=> setResdate(e.target.value)}/>
<label htmlFor='res-time'>Choose time:</label>
<Select id="restime" placeholder="restime" name="restime" value={restime} options={availableTimes} onChange={(e) => setRestime(e.target.value)} />
<label htmlFor="guests">Number of Guests</label>
<Select id="guests" placeholder='guests' name='guests' value={guests} options={Guests} onChange={(e) => setGuests(e.target.value)}/>
<label htmlFor="occasion">Occasion</label>
<Select id="occasion" placeholder='occasion' name="occasion" value={occasion} options={Occasion} onChange={(e) => setOccasion(e.target.value)}/>
</field>
</div>
<button type='submit'>Submit</button>
</fieldset>
</form>
</div>
</>
)
}
export default Bookingform
I think you should read this article:
https://dev.to/smeijer/simple-form-handling-in-react-o72
But a better way than what you have written is using FormData :
function MyForm() {
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const values = Object.fromEntries(data.entries());
if (!Number(values.age)) {
alert('Your age must be a number');
return;
}
console.log('submitting', values);
};
return (
<form onSubmit={handleSubmit}>
<h1>Hi!</h1>
<p>Enter your name:</p>
<input type="text" name="username" />
<p>Enter your age:</p>
<input type="text" name="age" />
<br /><br />
<input type="submit" />
</form>
);
}
Obviously a much better way would be to create a Hook for yourself using FormData which you can see a full example of it in the article I mentioned above.
So after some more research and reading the mentioned article i came up with this Solution. Is this a valid way of lifting the State of my Component to its Parent?
const formReducer= (state, event) => {
return {
...state,
[event.name]: event.value
}
}
const BookinForm = (props) => {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
setSubmitting(true);
props.onSubmit(formData);
setTimeout (() => {
setSubmitting(false);
}, 3000)
};
const handleChange = event => {
const isCheckbox = event.target.type === "checkbox";
setFormData({
name: event.target.name,
value: event.target.value,
value: isCheckbox ? event.target.checked : event.target.value,
});
}
return (
<div className="Wrapper">
<h1>Reserve a Table</h1>
{submitting && <div>You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>:{value.toString()}</li>
))}
</ul>
</div> }
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
<label>
<p>Email</p>
<input name="email" onChange={handleChange}/>
</label>
<label>
<p>Telephone</p>
<input name="telephone" onChange={handleChange}/>
</label>
</fieldset>
<fieldset>
<label>
<p>Time</p>
<select name="Time" onChange={handleChange}>
<option>17:00</option>
<option>18:00</option>
<option>19:00</option>
<option>20:00</option>
<option>21:00</option>
<option>22:00</option>
</select>
</label>
<label>
<p>Date</p>
<input type="date" name="Time" onChange={handleChange}/>
</label>
<label>
<p>Guests</p>
<input type="number" name="count" min="1" max="9" onChange={handleChange} step="1"/>
</label>
<label>
<p>Choose Occasion</p>
<select>
<option>Anniversary</option>
<option>Birthday</option>
</select>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
it does function as intended as i can Console log the Data from my Form inside my Parent component if i tell it to use this function On Submit:
const getData = (data) => {
console.log("Coming from Parent", data)
};
Any advice still here or does this code look fine for more experienced people?

how to use error values in return function with their names React JS

I'm new in react and I have faced a problem, I want to use the state variable with names and names are similar to inputs names
here in my app.js code
import React, { useRef, useState } from "react";
import logo from "./logo.png";
import vector from "./Vector.png";
import "./App.css";
function App() {
const inputFile = useRef(null);
const [inputValue, setValue] = useState({});
const [errorValue, setErrorValue] = useState({});
const fileHandler = (e) => {
setValue({
...inputValue,
[e.target.name]: URL.createObjectURL(e.target.files[0]),
});
};
const openImage = () => {
inputFile.current.click();
};
const inputHandler = (e) => {
setErrorValue({ ...errorValue, [e.target.name]: "" });
setValue({ ...inputValue, [e.target.name]: e.target.value });
};
const inputs = [
{
id: "name",
name: "name",
type: "text",
value: inputValue.name || "",
placeholder: "Enter name",
},
{
id: "email",
name: "email",
type: "email",
placeholder: "Enter Email",
},
{
id: "phone",
name: "phone",
type: "text",
value: inputValue.phone || "",
placeholder: "Enter Phone Number",
},
{
id: "headquater",
name: "headquater",
type: "text",
placeholder: "Headquater",
},
{
id: "about",
name: "about",
type: "text",
placeholder: "About Company",
},
];
const add = async () => {
const isEmailError = emailValidator(inputValue.email);
const isNameError = nameValidator(inputValue.name);
const isPhoneError = phoneValidator(inputValue.phone);
const obj = errorValue;
if (isEmailError) {
obj.email = isEmailError;
}
if (isNameError) {
obj.name = isNameError;
}
if (isPhoneError) {
obj.phone = isPhoneError;
}
setErrorValue(obj);
};
const show = async () => {
const obj = errorValue;
console.log(obj);
if (!obj.email && !obj.name && !obj.phone) {
console.log(inputValue);
}
};
const emailValidator = (text) => {
const regex = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
if (!text) {
return "Email Feild Required";
} else if (!regex.test(text)) {
return "Email not in valid format";
} else {
return "";
}
};
const nameValidator = (text) => {
const regex = /[^A-Za-z\s]/gi;
if (!text) {
return "Name Feild Required";
} else if (regex.test(text)) {
return "Name feild only contain alphabatic values";
} else {
return "";
}
};
const phoneValidator = (text) => {
const regex = /^([+]?[\s0-9]+)?(\d{3}|[(]?[0-9]+[)])?([-]?[\s]?[0-9])+$/i;
if (!text) {
return "Phone Feild Required";
} else if (!regex.test(text)) {
return "Phone feild only contain numeric values";
} else {
return "";
}
};
return (
<div className="responsive">
<div className="column-left">
<img src={logo} alt="" />
</div>
<div className="column-right">
<div className="center_item">
<h1 style={{ color: "#fff" }}>Set up account</h1>
<div className="img-center" onClick={openImage}>
<img
className={inputValue.image ? "fileImage" : ""}
src={inputValue.image ? inputValue.image : vector}
alt=""
/>
<input
onChange={fileHandler}
ref={inputFile}
className="p-text"
type="file"
name="image"
id="image"
/>
<p className={inputValue.image ? "p-text" : "file-text"}>
Upload File
</p>
</div>
{inputs.map((input, i) => {
return (
<div key={i}>
<input
{...input}
className="space"
onChange={inputHandler}
onBlur={add}
/>
// here I want to show errors
</div>
);
})}
<select
name="industory"
id="industory"
onChange={inputHandler}
className="space"
>
<option value="">Industory</option>
<option value="Industry 1">industory 1</option>
<option value="Industry 2">industory 2</option>
<option value="Industry 3">industory 3</option>
</select>
<br />
<select
name="country"
id="country"
onChange={inputHandler}
className="space"
>
<option value="">Country</option>
<option value="Pakistan">Pakistan</option>
<option value="Canada">Canada</option>
<option value="UK">UK</option>
</select>
<br />
<select
name="state"
id="state"
onChange={inputHandler}
className="space"
>
<option value="">State</option>
<option value="State 1">State 1</option>
<option value="State 2">State 2</option>
<option value="State 3">State 3</option>
</select>
<br />
<button className="space" onClick={show}>
Countinue
</button>
<br />
<br />
<p className="p-text">
Copyright 2022 Collage Nll. All right Reserved
</p>
</div>
</div>
</div>
);
}
export default App;
All things perfect input.name show inputs name perfectly(in console) but errorValue.input.name
throw an error Uncaught TypeError: Cannot read properties of undefined (reading 'name')
Thanks in Advance to all of those who reply
finally, I solved it
just add errorValue[input.name] to get specific error name value
{inputs.map((input, i) => {
return (
<div key={i}>
<input
{...input}
className="space"
onChange={inputHandler}
onBlur={add}
/>
<span>{errorValue[input.name]}</span>
</div>
);
})}

Onchange event in select should change others inputs field value?

As a Begineer in a react,here what i want to do is when i select options from the react-select option the i want to dispaly that selected options label in the text input down above on both field but got problem with here onchanges event How to change value in text field from the select options along with selected value?
Code
import React, { useState } from "react";
const initialValues = [{ number: "", options: "" }];
const countries = [
{ id: 1, name: USA },
{ id: 2, name: Russia },
{ id: 3, name: UK },
];
const Newrow = (props) => {
const [number, setNumber] = useState("");
const [options, setoption] = useState([]);
const [options1, setoption1] = useState("");
const addRow = () => {
let _row = {
number: "",
options: "",
};
props.setData(_row);
};
return (
<div>
<input
type="number"
value={number}
onChange={(e) => {
setNumber(e.target.value);
}}
/>
<input
type="text"
className="input"
value={options}
onChange={options1}
/>
<input
type="text"
className="input"
value={options}
onChange={options1}
/>
</div>
);
};
export default function App(props) {
const [data, setData] = useState([]);
const addRow = (row) => {
setData([...data, row]);
};
return (
<div className="App">
<Select
options={countries}
value={{
label: name,
value: id,
}}
onChange={(e) => {
const res = {
id: e.value,
name: e.label,
};
console.log(res);
setoption1(res);
}}
></Select>
{[...data, ...initialValues].map((row, idx) => {
return (
<Newrow
setData={addRow}
data={row}
key={idx}
delrow={idx}
options1={options1}
/>
);
})}
<button
type="submit"
onClick={() => addRow({ number: "", options: "" })}
className="btn btn-success"
>
Add
</button>
</div>
);
}
You appear to be passing in options1 (a string) into the onChange handler of your text inputs. The onChange handler should only take a function, that will be triggered when those inputs change.
You can discard the onChange={options1} property and change the value property to value={options1}.
Change this:
<input type="text"
className="input"
value={options}
onChange={options1}
/>
to this:
<input type="text"
className="input"
value={options1}
/>

How to a make dynamic form in React

I'm learning React. My target is to make this object:
"Phones": [
{
"Type": "Mobile",
"Phone_Number": "6546543"
},
{
"Type": "Home",
"Phone_Number": "6546543"
}
]
I did some research and I followed this YouTube video ReactJS - Dynamically Adding Multiple Input Fields.
These values I'll fetch from a form. Th user can keep on adding as many numbers as he wants. My code is:
render() {
return (
<div>
<h1>The Form</h1>
<label>Contact</label>
{this.state.phones.map((phone, index) => {
return (
<div key={index}>
<input onChange={(e) => this.handleChange(e)} value={phone} />
<select>
{this.phoneTypes.map((phoneType, index) => {
return (
<option key={index} value={phoneType}>
{phoneType}
</option>
);
})}
</select>
<button onClick={(e) => this.handleRemove(e)}>Remove </button>
</div>
);
})}
<hr />
<button onClick={(e) => this.addContact(e)}>Add contact</button>
<hr />
<button onClick={(e) => this.handleSubmit(e)}>Submit</button>
</div>
);
}
I'm not pasting the entire code here as I've already created a stackblitz. My code is not working as expected. Please pitch in.
You need to pass index in the handleChange function for input field. I guess you missed that part from video.This will make the input field editable.
<input
onChange={(e) => this.handleChange(e, index)}
value={phone}
/>
For the second part to get the expected object array I have updated some code, please check:
import React, { Component } from 'react';
class Questionnaire extends Component {
state = {
phones: [{ type: '', number: '' }],
};
phoneTypes = ['Mobile', 'Home', 'Office'];
addContact() {
this.setState({ phones: [...this.state.phones, { type: '', number: '' }] });
}
handleChange(e, index) {
this.state.phones[index]['number'] = e.target.value;
this.setState({ phones: this.state.phones });
}
handleRemove(index) {
this.state.phones.splice(index, 1);
console.log(this.state.phones, '$$$');
this.setState({ phones: this.state.phones });
}
handleSubmit(e) {
console.log(this.state, '$$$');
}
render() {
return (
<div>
<h1>The Form</h1>
<label>Contact</label>
{this.state.phones.map((phone, index) => {
return (
<div key={index}>
<input
onChange={(e) => this.handleChange(e, index)}
value={phone.number}
name="number"
/>
<select>
{this.phoneTypes.map((phoneType, index) => {
return (
<option key={index} value={phoneType} name="type">
{phoneType}
</option>
);
})}
</select>
<button onClick={(e) => this.handleRemove(e)}>Remove </button>
</div>
);
})}
<hr />
<button onClick={(e) => this.addContact(e)}>Add contact</button>
<hr />
<button onClick={(e) => this.handleSubmit(e)}>Submit</button>
</div>
);
}
}
export default Questionnaire;
You can use input objects like formik
import { useState } from 'react';
const Component = () => {
const [inputs, setInputs] = useState({ // Initial inputs
email: "",
phoneNumber: "",
});
const handleSubmit = (e) => {
e.preventDefault();
// Do
};
const handleChange = (e) => {
setInputs({...inputs, [e.target.id]: e.target.value});
};
const addInput = () => {
const name = window.prompt("Type a name", "");
setInputs({...inputs, [name]: ""});
};
return (
<form onSubmit={handleSubmit}>
{object.keys(inputs).map((name, i) => {
<input type="text" onChange={handleChange} name={name} value={inputs[name]} />
})}
<button
type="button"
onClick={addInput}
>
Add Input
</button>
</form>
);
}

How to Create 3(country,state and city) dropdowns that populates data with each other in react js

I want to create 3 dropdowns such as country,state,city.Based on selection in country option it should populate the state and based on country and state selected option the city dropdown should populate itself in react js. Thanks in Advance
i want add more states here in the usa country
componentDidMount() {
this.setState({
countries: [
{
name: "Germany",
states: [
{
name: "A",
cities: ["Duesseldorf", "Leinfelden-Echterdingen", "Eschborn"]
}
]
},
{ name: "Spain", states: [{ name: "B", cities: ["Barcelona"] }] },
{ name: "USA", states: [{ name: "C", cities: ["Downers Grove"] }] },
{
name: "Mexico",
states: [{ name: ["D", "F", "H"], cities: ["Puebla"] }]
},
{
name: "India",
states: [
{ name: "E", cities: ["Delhi", "Kolkata", "Mumbai", "Bangalore"] }
]
}
]
});
}
changeCountry(event) {
this.setState({ selectedCountry: event.target.value });
this.setState({
states: this.state.countries.find(
(cntry) => cntry.name === event.target.value
).states
});
}
changeState(event) {
this.setState({ selectedState: event.target.value });
const stats = this.state.countries.find(
(cntry) => cntry.name === this.state.selectedCountry
).states;
this.setState({
cities: stats.find((stat) => stat.name === event.target.value).cities
});
}
i want to display more states and city in one country (3 dropdowns)
render() {
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<label>Country</label>
<select
placeholder="Country"
value={this.state.selectedCountry}
onChange={this.changeCountry}
>
<option>--Choose Country--</option>
{this.state.countries.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</select>
</div>
<div>
<label>State</label>
<select
placeholder="State"
value={this.state.selectedState}
onChange={this.changeState}
>
<option>--Choose State--</option>
{this.state.states.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</select>
</div>
<div>
<label>City</label>
<select placeholder="City">
<option>--Choose City--</option>
{this.state.cities.map((e, key) => {
return <option key={key}>{e}</option>;
})}
</select>
</div>
</div>
);
}
}
Your solution is in a good direction. Though you can simplify it. Here's an example on how you can do it using React Hooks.
https://codesandbox.io/s/sweet-monad-kzp3p?file=/src/App.js
We start off by creation 3 states:
const [selectedCountry, setSelectedCountry] = React.useState();
const [selectedState, setSelectedState] = React.useState();
const [selectedCity, setSelectedCity] = React.useState();
const availableState = data.countries.find((c) => c.name === selectedCountry);
const availableCities = availableState?.states?.find(
(s) => s.name === selectedState
);
Normally their values will be undefined. Every time you make a selection in the dropdown, we can update them. When updated, we can use the data to find the corresponding states (in availableState). And if a state has been selected, we can use that property to find the cities for this state.
The ?.states is a null check. So if the availableState has not been set yet, we won't try to call any functions on it.
This way we can use the same properties for both rendering and finding the correct data. Removing a lot of update complexity.
The render would look like this:
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<label>Country</label>
<select
placeholder="Country"
value={selectedCountry}
onChange={(e) => setSelectedCountry(e.target.value)}
>
<option>--Choose Country--</option>
{data.countries.map((value, key) => {
return (
<option value={value.name} key={key}>
{value.name}
</option>
);
})}
</select>
</div>
<div>
<label>State</label>
<select
placeholder="State"
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
>
<option>--Choose State--</option>
{availableState?.states.map((e, key) => {
return (
<option value={e.name} key={key}>
{e.name}
</option>
);
})}
</select>
</div>
<div>
<label>City</label>
<select
placeholder="City"
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
>
<option>--Choose City--</option>
{availableCities?.cities.map((e, key) => {
return (
<option value={e.name} key={key}>
{e}
</option>
);
})}
</select>
</div>
</div>
);

Resources