React:获取地图内部并修改 useState 数组
React: Fetching inside a map and modifying a useState array
你们能帮我做一个基本的 React 应用吗:)
我想读取一个 json 文件:
[
{
"name": "Google",
"url": "www.google.com",
"status": "to evaluate"
},
{
"name": "Bing",
"url": "www.bing.com",
"status": "to evaluate"
},
etc.
]
阅读时,我想获取 url 以填充此 json 文件中的状态
最后,我只想创建一个包含两列的 table:第一列是带有 url 的名称,第二列是状态
我试过了但是没用 :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
顺便说一句,我真的很想用钩子来做:)
所以,我除了看到这个 table,但页面是空白的:/
您应该将jsonArray 更改为jsonData 作为参数并将初始状态设置为null。
目前您在调用函数时传递的是状态 (jsonArray) 而不是 jsonData。
此外,您应该将对 fetchData 的调用放在 useEffect 中,并将 jsonData 作为参数:
useEffect(() => fetchData(jsonData), [jsonData, fetchData]);
这会变成一个循环吗?组件加载 -> fetchData()
被调用 -> fetchData()
更新状态 -> 组件重新加载 -> 重复。
您可以使用带有空数组的 useEffect()
钩子作为仅 运行 一次的第二个参数:
useEffect(() => {
//Update json as you were doing;
}, []);
正如其他人所指出的,在这种情况下您需要使用 useEffect
。否则你的 fetchData
将在你每次更新状态时被一遍又一遍地调用。我还会更改您调用那些 URL 以获取其 http 状态的方式,以使用 promises(只是因为我喜欢 promises)。以下是我将如何编写您的代码:
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>
);
}
请注意,我使用的是常规 <table>
而不是您的预建元素 <Table>
,因为我没有。这是一个沙箱:https://codesandbox.io/s/confident-jang-yrxv3?file=/src/App.js:285-286
fetch
return 是一个 promise,它在解决后会产生获取响应对象。所以如果你只有一次获取,你可以这样调用它:
fetch(url).then(response => {//do stuff here with response object})
但是假设您有多个 fetch
命令,就像您在您的案例中所做的那样。你如何用一个 then
解决它们。这就是 Promise.all()
派上用场的地方。这个想法是从你的 fetch
命令中获得所有的承诺,然后一次性解决它们。因此:
const promises = jsonData.map((url, i) => {
return fetch(url.url).then((r) => ({ fetch: r, index: i }));
});
promises
将是 map
return 中所有 fetch
命令的承诺数组。这里需要注意的是,我还 return 特定 URL 的索引,以便稍后您可以使用它来查看哪个 URL 对应于哪个 HTTP 代码。最后,您使用以下方法解决所有问题:
Promise.all(promises)
.then((result) => {
...
...
简单吧?
你们能帮我做一个基本的 React 应用吗:) 我想读取一个 json 文件:
[
{
"name": "Google",
"url": "www.google.com",
"status": "to evaluate"
},
{
"name": "Bing",
"url": "www.bing.com",
"status": "to evaluate"
},
etc.
]
阅读时,我想获取 url 以填充此 json 文件中的状态
最后,我只想创建一个包含两列的 table:第一列是带有 url 的名称,第二列是状态
我试过了但是没用 :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
顺便说一句,我真的很想用钩子来做:)
所以,我除了看到这个 table,但页面是空白的:/
您应该将jsonArray 更改为jsonData 作为参数并将初始状态设置为null。
目前您在调用函数时传递的是状态 (jsonArray) 而不是 jsonData。 此外,您应该将对 fetchData 的调用放在 useEffect 中,并将 jsonData 作为参数:
useEffect(() => fetchData(jsonData), [jsonData, fetchData]);
这会变成一个循环吗?组件加载 -> fetchData()
被调用 -> fetchData()
更新状态 -> 组件重新加载 -> 重复。
您可以使用带有空数组的 useEffect()
钩子作为仅 运行 一次的第二个参数:
useEffect(() => {
//Update json as you were doing;
}, []);
正如其他人所指出的,在这种情况下您需要使用 useEffect
。否则你的 fetchData
将在你每次更新状态时被一遍又一遍地调用。我还会更改您调用那些 URL 以获取其 http 状态的方式,以使用 promises(只是因为我喜欢 promises)。以下是我将如何编写您的代码:
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>
);
}
请注意,我使用的是常规 <table>
而不是您的预建元素 <Table>
,因为我没有。这是一个沙箱:https://codesandbox.io/s/confident-jang-yrxv3?file=/src/App.js:285-286
fetch
return 是一个 promise,它在解决后会产生获取响应对象。所以如果你只有一次获取,你可以这样调用它:
fetch(url).then(response => {//do stuff here with response object})
但是假设您有多个 fetch
命令,就像您在您的案例中所做的那样。你如何用一个 then
解决它们。这就是 Promise.all()
派上用场的地方。这个想法是从你的 fetch
命令中获得所有的承诺,然后一次性解决它们。因此:
const promises = jsonData.map((url, i) => {
return fetch(url.url).then((r) => ({ fetch: r, index: i }));
});
promises
将是 map
return 中所有 fetch
命令的承诺数组。这里需要注意的是,我还 return 特定 URL 的索引,以便稍后您可以使用它来查看哪个 URL 对应于哪个 HTTP 代码。最后,您使用以下方法解决所有问题:
Promise.all(promises)
.then((result) => {
...
...
简单吧?