Reputation: 950
I am a beginner in React and using React hooks. I have a function component that needs to show some info on the page. I receive the and return the jobs array correctly and I can iterate in it and show its info successfully, but want to receive another array too:location
. this array is based on the jobs
array: I need to receive jobs
first, then get its location id and then receive info and put it into my location array to be used in my info page later. the problem is, that in my form when I want to iterate in the locations, instead of actually showing the location.country
in front of my text, it repeats the JSX text 'Location:' three times in my form (I have 3 job objects). I know that I have to save that location array in my useEffect
but it only accepts 2 parameters. Here's my code:
function fetchJSON (...args) {
return fetch('/api/jobs/list-jobs', { headers: headers })
.then(r => {
if (!r.ok) {
throw new Error('HTTP error ' + r.status)
}
return r.json()
})
}
function useJobs () {
const [jobs, setJobs] = React.useState([])
const [locations, setLocations] = React.useState([])
React.useEffect(() => {
fetchJSON('/api/jobs/list-jobs', { headers: headers })
.then(setJobs)
}, [])
React.useEffect(() => {
for (const job of jobs) {
fetchJSON(`/api/jobs/view-location/${job.location}/`, { headers: headers })
.then(setLocations)// *** Need to handle #6 here
}
}, [jobs])
return [jobs, locations]
}
export default function Jobs () {
const classes = useStyles()
const [jobs, locations] = useJobs()
return (
{jobs.map(job => (
<>
<div className={classes.root} key={job.id}>
<Row>
<Col style={{ color: 'black' }}>Title:{job.title} </Col>
<Col>Company Name:{job.company_name} </Col>
<Col style={{ color: 'black' }}>Internal Code:{job.internal_code} </Col>
</Row>
{locations.map(location => (
<Col key={location.id} style={{ color: 'black' }}>Location:{location.country}</Col>))
}
I have 3 of these job objects and for each one, I want to print the correct location according to the job.location
location array that I received in the useEffect. all of the other fields of the jobs
array are working properly. how to implement the location part correctly?
Upvotes: 0
Views: 199
Reputation: 281754
You fetch the data for locations for individual jobs but only overrider the locations state when you use .then(setLocations)
You must instead store locations as an object with job id as key
function useJobs () {
const [jobs, setJobs] = React.useState([])
const [locations, setLocations] = React.useState({})
React.useEffect(() => {
fetchJSON('/api/jobs/list-jobs', { headers: headers })
.then(setJobs)
}, [])
React.useEffect(() => {
for (const job of jobs) {
fetchJSON(`/api/jobs/view-location/${job.location}/`, { headers: headers })
.then((locations) => {
setLocations(prev => ({...prev, [job.id]: locations}))
})
}
}, [jobs])
return [jobs, locations]
}
However a better way for you would be to use Promise.all to update all the locations in one go
function useJobs () {
const [jobs, setJobs] = React.useState([])
const [locations, setLocations] = React.useState({})
React.useEffect(() => {
fetchJSON('/api/jobs/list-jobs', { headers: headers })
.then(setJobs)
}, [])
` React.useEffect(() => {
async function fetchLocations() {
const promises = [];
for (const job of jobs) {
promises.push(fetchJSON(`/api/jobs/view-location/${job.location}/`, { headers: headers })
.then((locations) => {
return {[job.id]: locations }
}))
}
const data = await Promise.all(promises);
setLocations(prev => Object.assign({}, ...data))
}
fetchLocations();
}, [jobs])
return [jobs, locations]
}
Now you must note that location corresponding to each job id is just an object and not an array(as per discussion in chat), so you don't need to map
{locations[job.id] && <Col key={locations[job.id].id} style={{ color: 'black' }}>Location:{locations[job.id].country}</Col>}
Upvotes: 1