I had a json file like this:
[
{
"name": "google",
"route": "/google",
"url": "www.google.com"
},
{
"name": "bing",
"route": "/bing",
"url": "www.bing.com"
},
{
"name": "duckduckgo",
"route": "/duckduckgo",
"url": "www.duckduckgo.com"
}
]
I wanted to fetch each url of this json file and show the status of the fetchs in a table. I had this code that worked:
// Construct an object with the JSON data
const [data, setData] = useState(jsonData)
// We need to put the fetch call inside useEffect
// otherwise the fetchData will be called over and over every time the state is updated
useEffect(() => {
let headers = new Headers()
//headers.append('Content-Type', 'application/json')
//headers.append('Accept', 'application/json')
headers.append('Origin', 'http://localhost:8080')
// Array with all the promises from each fetch
// @r contains all the info about the fetch, we are going to use the r.status later
// @i is the index of the url in the JSON file
const promises = jsonData.map((url, i) => {
return fetch(url.route, {
mode: 'no-cors',
headers: headers,
}).then((r) => ({
fetch: r,
index: i,
}))
})
Promise.all(promises)
.then((result) => {
// Iterates through each promise and replaces the status value from the JSON file
// with the status value from the fetch
const new_data = result.map((d) => {
jsonData[d.index].status = d.fetch.status
return jsonData[d.index]
})
setData(new_data)
})
.catch((error) => {
console.log('Error: ', error)
})
}, [])
Now, I updated a bit my json file, it is a little bit more complicated:
[
{
"section": "Sonarqube",
"img": "sonarqube.png",
"urls": [
{
"name": "SonarQube",
"route": "/sonarqube",
"url": "https://sonarqube-enterprise.com"
},
{
"name": "SonarQube1",
"route": "/sonarqube",
"url": "https://sonarqube-enterprise1.com"
},
{
"name": "SonarQub2e",
"route": "/sonarqube",
"url": "https://sonarqube-enterprise2.com"
}
]
},
{
"section": "Twistlock",
"img": "twistlock.png",
"urls": [
{
"name": "Twistlock",
"route": "/twistlock",
"url": "https://twistlock.com"
}
]
}
]
I'm trying to change my code to read the urls array inside each section but I can't figure out how to make it work. Can you please help me. This is where I am for now:
const [data, setData] = useState(jsonData)
useEffect(() => {
const promises = jsonData.map((section, s) => {
return section.urls.map((url, i) => {
return fetch(url.route, {
mode: 'no-cors',
}).then((r) => ({
fetch: r,
index: i,
sectionId: s,
}))
})
})
Promise.all(promises)
.then((result) => {
const new_data = result.map((section) => {
section.map((d) => {
jsonData[d.sectionId].urls[d.index].status = d.fetch.status
jsonData[d.sectionId].urls[d.index].statusText = d.fetch.statusText
return jsonData[d.sectionId].urls[d.index]
})
})
setData(new_data)
})
.catch((error) => {
console.log('Error: ', error)
})
}, [])
I believe that I'm almost there but I really don't understand how to make this work :O can you help me please
CodePudding user response:
You return array of array in nested map nor promises in your jsonData.map
[
[
Promise { <pending> },
Promise { <pending> },
Promise { <pending> }
],
[ Promise { <pending> } ]
]
You need to add .flat()
to flatten array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)
const promises = jsonData.map((section, s) => {
return section.urls.map((url, i) => {
return fetch(url.route, {
mode: 'no-cors',
}).then((r) => ({
fetch: r,
index: i,
sectionId: s,
}))
})
}).flat()
CodePudding user response:
Try like this. You can first loop the outer array and then do the Promises.all
. And destructure the properties you want.
const jsonData = [ { section: "Sonarqube", img: "sonarqube.png", urls: [ { name: "SonarQube", route: "/sonarqube", url: "https://sonarqube-enterprise.com" }, { name: "SonarQube1", route: "/sonarqube", url: "https://sonarqube-enterprise1.com" }, { name: "SonarQub2e", route: "/sonarqube", url: "https://sonarqube-enterprise2.com" } ] }, { section: "Twistlock", img: "twistlock.png", urls: [ { name: "Twistlock", route: "/twistlock", url: "https://twistlock.com" } ] } ];
const App = () => {
const [data, setData] = React.useState(jsonData);
React.useEffect(() => {
const promises = jsonData.map((section, s) => {
return section.urls.map((url, i) => {
return fetch(url.route, {
mode: "no-cors"
}).then((r) => ({
fetch: r,
index: i,
sectionId: s
}));
});
});
promises.forEach((promises2) => {
Promise.all(promises2).then((result) => {
const copyJsonData = [...jsonData];
result.forEach(
({
sectionId,
index,
fetch: {
status,
statusText
}
}) => {
copyJsonData[sectionId].urls[index].status = status;
copyJsonData[sectionId].urls[index].statusText = statusText;
}
);
setData(copyJsonData);
});
});
}, []);
console.log(jsonData);
return <div></div>;
};
ReactDOM.render( < App / > , document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
CodePudding user response:
I don't know why you complicate things, try to separate the code so it could be understandable and easy for you to read.
Anyway here is how I would do it.
const promises = [];
jsonData.map((section, s) => {
section.urls.map((url, i) => {
// create it here so the context gets in the right place.
let data = {
index: i,
sectionId: s,
}
// use push is more understandable
promises.push(fetch(url.route, {
mode: 'no-cors',
}).then((r) => ({ ...data, fetch: r })));
})
})