Can you guys please help me with a basic React app :) I want to read a json file:
[
{
"name": "Google",
"url": "www.google.com",
"status": "to evaluate"
},
{
"name": "Bing",
"url": "www.bing.com",
"status": "to evaluate"
},
etc.
]
While reading it, I want to fetch the url to fill the status in this json file
finally, I just want to make a table that has two columns: the first is the name with the url, the second is the status
I tried this but it does not work :O
import React, { useState } from 'react'
import Table from 'react-bootstrap/Table'
import jsonData from './data/urls.json'
function ListUrls () {
const [jsonArray, setJsonArray] = useState(jsonData)
async function fetchData (array) {
try {
array.map (url => {
const response = await fetch(url.url)
setJsonArray(url.status = response.status)
})
} catch (error) {
console.log('Error', error);
}
fetchData(jsonArray)
return (
<div>
<Table>
<thead>
<tr>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{jsonArray.map(url => (
<tr>
<td>
<a href={url.url} target='_blank'}>
{url.name}
</a>
</td>
<td>
{url.status}
</td>
</tr>
))}
</tbody>
</Table>
</div>
)
}
export default ListUrls
Btw, I would really want to use hooks to do it :)
So, I would except to see this table, but the page is blank :/
As pointed out by the others, you will need to use useEffect
in this case. Otherwise your fetchData
will be called over and over every time you update the state. I would also change the way you call those URLs to get their http status to using promises (just cuz I like promises). Here is how I would write your code:
import { useEffect, useState } from "react";
import "./styles.css";
import jsonData from "./urls.json";
export default function App() {
const [data, setData] = useState(jsonData);
useEffect(() => {
const promises = jsonData.map((url, i) => {
return fetch(url.url).then((r) => ({ fetch: r, index: i }));
});
Promise.all(promises)
.then((result) => {
const new_data = result.map((d) => {
jsonData[d.index].status = d.fetch.status;
return jsonData[d.index];
});
setData(new_data);
})
.catch((e) => {
console.log("Handle error here");
});
}, []);
return (
<div>
<table>
<thead>
<tr>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{data.map((url, i) => (
<tr key={i}>
<td>
<a href={url.url} target={"_blank"}>
{url.name}
</a>
</td>
<td>{url.status}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
Note that I am using the regular <table>
instead of your prebuilt element <Table>
as I don't have it. Here is a sandbox: https://codesandbox.io/s/confident-jang-yrxv3?file=/src/App.js:285-286
fetch
returns a promise, which, when resolved, yields the fetch response object. So if you only have one fetch to do you can call it like so:
fetch(url).then(response => {//do stuff here with response object})
But let's say you have multiple fetch
commands as you do in your case. How do you resolve them with just one then
. Well that's where Promise.all()
comes in handy. The idea is to get all the promises from your fetch
commands and then resolve them all in one shot. As such:
const promises = jsonData.map((url, i) => {
return fetch(url.url).then((r) => ({ fetch: r, index: i }));
});
promises
will be an array of promises that all the fetch
commands inside the map
return. One caveat here is I also return the index of the specific URL so that later you can use it to see which URL corresponds to which HTTP code. And finally you resolve them all using:
Promise.all(promises)
.then((result) => {
...
...
Simple right?