【问题标题】:How to chain two Fetch API requests with Async/Await如何使用 Async/Await 链接两个 Fetch API 请求
【发布时间】:2021-03-16 22:56:45
【问题描述】:

我有一阵子想不通...(我是 React 新手,还在学习)

我需要使用两个 API:

第一个包含所有客户订单信息

{ "orders": [{ "id": "480cb439", "name": "订单名称", "clientId": 4 }, ... ] }

第二个根据第一个 API 的参数 返回客户端信息

{ "client": { "id": 4, "name": "John Doe" } }

目标非常简单 - 从两个 API 获取信息,将其呈现为包含订单和客户端信息的简单卡片。如何使用钩子在 React 中使用 async/await 执行此操作?

我的代码如下:

import React, { useState } from 'react';
import { useApi } from "./services/UseAPI";
import { Loading } from "./components/LoadingComponent";
import { Card, CardImg, CardText, CardBody, CardTitle, CardSubtitle, Button, CardHeader } from 'reactstrap';



function App() {

  const [searchTerm, setSearchTerm] = React.useState("");
  const handleChange = event => {
    setSearchTerm(event.target.value);
  };

  const Clients = ({clientId}) => {
  const url = `Clients API endpoint` + clientId;
  const param = "client";
  const { data, isLoading, hasError } = useApi(url, [], param);
  
  if (isLoading) return <Loading />;

  if (hasError)
    return (
    <div>
      <p>Failed to fetch worker info ????</p>
    </div>
    );

  if (data.name !== undefined && data.name.toLowerCase().includes(searchTerm.toLocaleLowerCase()))
    return ( 
      <div className="row">
        <div className="col-7 my-auto text-left">
          <div>{data.name}</div>
        </div>
      </div>
    );
    else return null;
  }

  const Orders = () => {
    const url = `Orders API endpoint`;
    const param = "orders";
    const { data, isLoading, hasError } = useApi(url, [], param);
    
    if (isLoading) return <Loading />;

    if (hasError)
      return (
      <div>
        <p>Failed to fetch work orders ????</p>
      </div>
      );
      return ( 
        data.map(order => 
            <div className="col-6">
              <Card key={order.id} className="text-center m-5">
                <CardHeader tag="h5">{order.name}</CardHeader>
                <CardBody>
                  <Clients clientId={order.clientId} searchTerm={searchTerm}  />
                </CardBody>
              </Card>
            </div>
          )
      );
  }

  return (
    <div className="row">
      <input className="col-10 offset-1" type="text" placeholder="Filter by client name..." value={searchTerm} onChange={handleChange} /> 
      <Orders />
    </div>
  );
}

export default App;

我用于从 API 获取的自定义钩子:

import { useEffect, useState } from "react";

export const useApi = (initialUrl, initialData, param) => {
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(true);
  const [hasError, setHasError] = useState(false);
  const [fetchedData, setFetchedData] = useState(initialData);

  useEffect(() => {
    let unmounted = false;

    const handleFetchResponse = response => {
      if (unmounted) return initialData;

      setHasError(!response.ok);
      setIsLoading(false);
      return response.ok && response.json ? response.json() : initialData;
    };

    const fetchData = () => {
      setIsLoading(true);
      return fetch(url)
        .then(handleFetchResponse)
        .catch(handleFetchResponse);
    };

    if (initialUrl && !unmounted)
      fetchData().then(data => !unmounted && setFetchedData(data[param]));

    return () => {
      unmounted = true;
    };
  }, [url]);

  return { isLoading, hasError, setUrl, data: fetchedData };
};

【问题讨论】:

  • 可以并行调用还是第二个调用依赖于第一个响应中的信息?
  • 第二次调用取决于第一次调用的信息...

标签: reactjs async-await react-hooks fetch


【解决方案1】:

先以更简单的方式使逻辑正常工作,然后尝试将代码提取到自定义 Hook 中。试试下面的代码,做一些必要的调整,看看能不能得到你期望的结果。

  function App() {

  const [searchTerm, setSearchTerm] = React.useState("");

  const handleChange = event => 
  {
    setSearchTerm(event.target.value);
  };

  const Clients = ({clientId}) => 
  {
  const url = `Clients API endpoint` + clientId;
  const param = "client";
  
  const [data, setData] = useState(null)
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState(false)


  const fetchApiData = async () =>
  {
      setIsLoading(true)
      try 
      {
        const response = await fetch(url)
        const responseJson = response.json
        setData(responseJson)
        setIsLoading(false)
      } 
      catch (error) 
      {
        setError(error)
      }        
  }

    useEffect(() =>
    {
      fetchApiData()
    }, [])
  
  if (isLoading) return <Loading />;

  if (hasError)
    return (
    <div>
      <p>Failed to fetch worker info ?</p>
    </div>
    );

  if (data.name !== undefined && data.name.toLowerCase().includes(searchTerm.toLocaleLowerCase()))
    return ( 
      <div className="row">
        <div className="col-7 my-auto text-left">
          <div>{data.name}</div>
        </div>
      </div>
    );
    else return null;
  }

  const Orders = () => {
    const url = `Orders API endpoint`;
    const param = "orders";


    const [data, setData] = useState(null)
    const [isLoading, setIsLoading] = useState(false)
    const [hasError, setError] = useState(false)
  
  
    const fetchApiData = async () =>
    {
        setIsLoading(true)
        try 
        {
          const response = await fetch(url)
          const responseJson = response.json
          setData(responseJson)
          setIsLoading(false)
        } 
        catch (error) 
        {
          setError(error)
        }        
    }
  
      useEffect(() =>
      {
        fetchApiData()
      }, [])





    
    if (isLoading) return <Loading />;

    if (hasError)
    return (
      <div>
        <p>Failed to fetch work orders ?</p>
      </div>
      );


      return ( 
        data.map(order => 
            <div className="col-6">
              <Card key={order.id} className="text-center m-5">
                <CardHeader tag="h5">{order.name}</CardHeader>
                <CardBody>
                  <Clients clientId={order.clientId} searchTerm={searchTerm}  />
                </CardBody>
              </Card>
            </div>
          )
      );
  }

  return (
    <div className="row">
      <input className="col-10 offset-1" type="text" placeholder="Filter by client name..." value={searchTerm} onChange={handleChange} /> 
      <Orders />
    </div>
  );
}

export default App;

【讨论】:

  • 谢谢!但我的问题更多是关于如何使用 async/await 来做到这一点?喜欢这里pluralsight.com/guides/…
  • 我已经用 async/await 和 try/catch 块更新了我的答案。试试这个,看看它是否有效
猜你喜欢
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多