Reputation: 13
I am new to react and have been having hard time implementing this dependent dropdown. I want to get the lists of states and filter out location that renders to the second dropdown. Below is what i tried implementing. The States and Location data is pulled from an API.
I was able to pull the states data but got stuck at implementing the dependent dropdown. Any help will be appreciated.
import React, { useState, useEffect } from 'react'
function NullView() {
// //API CALL
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
const [state, setState] = useState(null);
const [locations, setLocations] = useState([])
// console.log(count)
let loadLocations = (e) => {
e.preventDefault();
}
useEffect(() => {
fetch("/api/merchant/all/states/")
.then(res => res.json())
.then((result) => {
setIsLoaded(true);
setItems(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
if (state != null) {
fetch("/api/merchant/locations/2/")
.then(res => res.json())
.then((result) => {
setIsLoaded(true);
setLocations(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}
}, [])
return (
<div>
<form onChange={loadLocations}>
<label for="states">Choose a State:</label>
<select id="states" name="states" onChange={(e) => setState(e.target.value)}>
<option value="">Select State</option>
{items.map(states => (
<option key={states.id} value={states.id}>{states.name}</option>
))}
</select>
<label for="location">Choose a Location:</label>
<select id="location" name="location" onChange={(e) => setLocation(e.target.value)}>
<option value="">Select Location</option>
{Location.map(location => (
<option key={location.id} value={location.id}>{location.name}</option>
))}
</select>
</form>
</div>
)
}
export default NullView
Upvotes: 1
Views: 6492
Reputation: 229
try this
import React, { useState, useEffect } from 'react'
function NullView() {
// //API CALL
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [items, setItems] = useState([]);
const [state, setState] = useState(null);
const [locations, setLocations] = useState([])
//i set api location in state
const [apiLocation, setAPILocation] = useState(`/api/merchant/locations/`)
// console.log(count)
let loadLocations = (e) => {
e.preventDefault();
}
useEffect(() => {
fetch("/api/merchant/all/states/")
.then(res => res.json())
.then((result) => {
setIsLoaded(true);
setItems(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
fetch(apiLocation)
.then(res => res.json())
.then((result) => {
setIsLoaded(true);
setLocations(result);
},
(error) => {
setIsLoaded(true);
setError(error);
}
)
}, [])
checkUrlLocation(url, newValue) {
let splited = url.split('/')
splited[splited.length - 1] = newValue
return splited.join('/')
}
return (
<div>
<form onChange={loadLocations}>
<label for="states">Choose a State:</label>
<select id="states" name="states" onChange={(e) =>{
/* in this case, if you select i states, it will put state id in api location and re-run useEffect */
setAPILocation(checkUrlLocation(apiLocation,e.target.value))
setState(e.target.value)
}
} >
<option value="">Select State</option>
{items.map(states => (
<option key={states.id} value={states.id}>{states.name}</option>
))}
</select>
<label for="location">Choose a Location:</label>
<select id="location" name="location" onChange={(e) => setLocation(e.target.value)}>
<option value="">Select Location</option>
{locations.map(location => (
<option key={location.id} value={location.id}>{location.name}</option>
))}
</select>
</form>
</div>
)
}
export default NullView
Upvotes: 0
Reputation: 202605
You can break the two fetches into separate useEffect
callbacks. The first useEffect
callback fetches on component mount and updates the "state" options while the second useEffect
has a dependency on the currently selected "state" value.
useEffect(() => {
fetch("/api/merchant/all/states/")
.then((res) => res.json())
.then((result) => setItems(result))
.catch((error) => setError(error))
.finally(() => setIsLoaded(true));
}, []); // <-- fetch once when component mounts
useEffect(() => {
if (state) {
fetch(`/api/merchant/locations/${state}`) // <-- compute request URL
.then((res) => res.json())
.then((result) => setLocations(result))
.catch((error) => setError(error))
.finally(() => setIsLoaded(true));
}
}, [state]); // <-- fetch when state updates
I believe you've also a typo in your render, Location
should likely be locations
state.
return (
<div>
<form onChange={loadLocations}>
<label for="states">Choose a State:</label>
<select
id="states"
name="states"
onChange={(e) => setState(e.target.value)}
>
<option value="">Select State</option>
{items.map((states) => (
<option key={states.id} value={states.id}>
{states.name}
</option>
))}
</select>
<label for="location">Choose a Location:</label>
<select
id="location"
name="location"
onChange={(e) => setLocation(e.target.value)}
>
<option value="">Select Location</option>
{locations.map((location) => ( // <-- render locations state
<option key={location.id} value={location.id}>
{location.name}
</option>
))}
</select>
</form>
</div>
);
Upvotes: 2