I have some problems with a project. I want to display some data, that I retreive from an API, in a dropdown menu. I used the .map() function and jsx to display everything, but nothing is working, the dropdown menu stay empty whereas a console.log(cities) show me that my array is filled.
function CitiesList(){
var cities = citiesRequest();
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
If I change the way "cities" is fill for test, everything is working and the dropdown menu is filled :
function CitiesList(){
var cities = ["Berlin", "Paris", "London"];
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
Here is entire my code if that can help :
function Header(props){
return(
<header>
<h1 className="apiTitle">{props.name}</h1>
<CitiesList/>
</header>
);
}
function CitiesList(){
var cities = citiesRequest();
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
ReactDOM.render(
<Header name="Bike Mapper"/>,
document.getElementById('root')
);
function citiesRequest(){
var citiesArray = [];
fetch("https://api.jcdecaux.com/vls/v3/contracts?apiKey="+APIKEY)
.then((response) => response.json())
.then(function(data){
data.forEach(element => {
if(element.country_code == 'FR'){
citiesArray.push(element.name.toUpperCase());
}
});
})
return citiesArray;
}
Thanks
Your city array is always empty because you are not waiting for the API to send the response back. To achieve this you can use Promises
function Header(props){
return(
<header>
<h1 className="apiTitle">{props.name}</h1>
<CitiesList/>
</header>
);
}
async function CitiesList(){
var cities = await citiesRequest();
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
ReactDOM.render(
<Header name="Bike Mapper"/>,
document.getElementById('root')
);
async function citiesRequest(){
return new Promise((resolve,reject)=>{
var citiesArray = [];
fetch("https://api.jcdecaux.com/vls/v3/contracts?apiKey="+APIKEY)
.then((response) => response.json())
.then(function(data){
data.forEach(element => {
if(element.country_code == 'FR'){
citiesArray.push(element.name.toUpperCase());
}
});
resolve(citiesArray);
})
})
}
Using Hooks
import React, { useState, useEffect } from 'react';
function Header(props){
const [cities, setCities] = useState([])
useEffect(()=>{
citiesRequest().then(data=>{
setCities(data)
});
},[])
return(
<header>
<h1 className="apiTitle">{props.name}</h1>
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}
</option>)}
</select>
</form>
</header>
);
}
ReactDOM.render(
<Header name="Bike Mapper"/>,
document.getElementById('root')
);
async function citiesRequest(){
return new Promise((resolve,reject)=>{
var citiesArray = [];
fetch("https://api.jcdecaux.com/vls/v3/contracts?apiKey="+APIKEY)
.then((response) => response.json())
.then(function(data){
data.forEach(element => {
if(element.country_code == 'FR'){
citiesArray.push(element.name.toUpperCase());
}
});
resolve(citiesArray);
})
})
}
I think the problem is that by the time that data is returned from your citiesRequest function, the component rendered already and has no indication that it should re-render.
You can try to:
citiesRequest function to return the data directlyuseEffect (with empty dependency array - to call it just once when the component mounts) in your componentsetCities).When state changes, React will know it should re-render.
function CitiesList(){
const [cities, setCities] = useState([]);
useEffect(() => {
const getData = async () => {
const data = await citiesRequest();
setCities(data);
}
getData();
}, [])
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
function citiesRequest(){
return fetch("https://api.jcdecaux.com/vls/v3/contracts?apiKey="+APIKEY)
.then((response) => response.json())
.then(function(data){
return data
})
}
using useState useEffect to have the page re-render on cities state change
function CitiesList(){
const [cities, setCities] = React.useState([])
React.useEffect(()=>{
if(!cities.length){
citiesRequest().then(result=>setCities(result)).catch(console.log)
}
},[cities])
return (
<form id="cityChoice">
<label for="citySelected">Ville : </label>
<select name="citiesList" id="citySelected">
{cities.map((city) => <option key={city} value={city}>{city}</option>)}
</select>
</form>
);
}
function citiesRequest(){
return new Promise((resolve, reject)=>{
var citiesArray = [];
fetch("https://api.jcdecaux.com/vls/v3/contracts?apiKey="+APIKEY)
.then((response) => response.json())
.then(function(data){
data.forEach(element => {
if(element.country_code == 'FR'){
citiesArray.push(element.name.toUpperCase());
}
});
}).catch(err=>reject(err))
resolve(citiesArray);
})
}