I feel like this should be simple, but I’ve been struggling with this and would appreciate some help.
I’m trying to generate a drowdown list from a field in a data table I fetched using Axios. The data table loads and renders just fine, but React does not recognize the list as an array and therefore can not map the items. Both the data table and list are generated and stored in state in the same function at the same time.
Below is my code. Any suggestions on how to do this is much appreciated. Thanks:
import React, {useState, useEffect} from 'react'
import db from './db.js' //<--I have my axios db connection in a seperate file
function App() {
const [industryData, setIndustryData] = useState([]) //
const [industryList, setIndustryList] = useState([]) //<--This is the list I want
const getData= async(req, res)=>{
const response = await db.get('/getTable/industries')
const data = response.data;
console.log(data) // <--Data loads fine
setIndustryData(data) // <--Data renders fine
//I want to get a dropdown list from one of the fields in the data above
let list = []
data.forEach(row=>{
list.push(row.industry)
})
console.log(list) //<--this works
const industries = new Set(list)
console.log(industries) //<---this works
setIndustryList(industries)
console.log(industryList) //<--This is empty.
}
useEffect(()=>{
getData()
},[])
return (
<div className="App">
//This is the dropdown I want but it's not working
<select>
{industryList.map(item=>( //<---React doesn't recognize this object
<option key={industryList.indexOf(item)}>{item}</option>
))}
</select>
//But rendering the original data table works. But I don't want to do this because I don't get unique values. I need a set. Also it's just cleaner to use a list than an entire data set.
<select>
{industryData.map(item=>( //<---This works fine
<option key={industryData.indexOf(item)}>{item.industry}</option>
))}
</select>
</div>
);
}
export default App;
2
Answers
It looks like the issue might be related to the asynchronous nature of the
getData
function. When you callsetIndustryList(list)
, it may not have the updatedindustryData
immediately. What you can do is, insidegetData
instead ofindustryData.forEach
you can get the data fromdata.foreach
. Here is the updatedgetData
function you can use.So there are two problems at play here. Let’s first start with the root problem:
The reason
map()
does not work is becauseindustryList
is aSet
.Set
objects do not have amap()
method. (They also do not have anindexOf()
method.)If you aren’t using any
Set
features (apart from it removing duplicate values as part of the constructor). The easiest fix would be to convertindustries
to an array before setting it as state.This can be done with
Array.from(industries)
or spreading the contents into an array[...industries]
.If you are using some of the set features you’ll want to do this same conversion, but during rendering instead.
Array.from()
also allows us to pass a second argumentmapFn
. This does the same asmap()
would do, but does it during the Set to Array conversion. Causing one less iteration.Now, let’s move on to the second problem that arose when you where trying to debug the above.
Why is
industryList
empty?Let’s look at the
useState
documentation for this one:When you log
industryList
directly after usingsetIndustryList()
, it does not yet hold its new value.