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) => {
            ...
            ...

简单吧?