在 UseEffect 中为两个 API 调用设置 Promise

Posted

技术标签:

【中文标题】在 UseEffect 中为两个 API 调用设置 Promise【英文标题】:Setting up Promise for two API calls in UseEffect 【发布时间】:2021-03-31 20:57:01 【问题描述】:
const [activities, setActivities] = useState([""]);
const [contact, setContact] = useState();
const currentActivity = activities[0]
let contactId = currentActivity.contacts


useEffect(() => 

    API.getActivities()
      .then(res => 
        setActivities(res.data)
      ).catch((err) => console.log(err))

      API.getContact(contactId)
      .then(res => 
        setActivities(res.data)
      ).catch((err) => console.log(err))

, []);


console.log(currentActivity)
console.log(contactId)
console.log(contact)

第一个调用设置包含联系人 ID 的活动状态。我需要 id 来运行第二次呼叫以获取联系信息。我相信我需要设定一个承诺,但我被卡住了。当我运行代码时,联系人 ID 没有及时返回以拉取联系人。另一种解决方案可能是调用所有联系人并循环以匹配联系人返回的 id。当我尝试过时,“联系人”状态也返回未定义。

【问题讨论】:

查看 init async await,或者您可以将第二个 promise 嵌套在第一个 promise 的 .then 【参考方案1】:

如果这有帮助,请告诉我。我还没有测试过,可能需要做一些小改动。

useEffect(async() => 
    const resActivities=await API.getActivities()
    setActivities(resActivities.data) 
    const promises=resActivities.data.map(item=>API.getContact(item.contactId)) // assuming contactId property exists
    const resp=await Promises.all(promises)  
    setContact(resp.flat())
, []);

【讨论】:

【参考方案2】:

我在 Wirespec 上创建了几个 API 来生成可以测试的响应。您可以使用 Wirespec 免费创建自己的 API。

getRandomUser AP我: https://wirespec.dev/Wirespec/projects/apis/***/apis/getRandomUserId

getRandomUser 响应: https://api.wirespec.dev/wirespec/***/getrandomuserid

getUserDetails API: https://wirespec.dev/Wirespec/projects/apis/***/apis/getUserDetails

getUserDetails 响应: https://api.wirespec.dev/wirespec/***/getuserdetails?id=3

这里是测试代码。提供了两种解决方案:

let userDetails;

new Promise(function (resolve) 
    let user = JSON.parse(httpGet("https://api.wirespec.dev/wirespec/***/getrandomuserid"));
    resolve(user);
).then(function (user) 
    userDetails = JSON.parse(httpGet("https://api.wirespec.dev/wirespec/***/getuserdetails?id=" + user.id));
    console.log("firstName: " + userDetails.firstName + ", lastName: " + userDetails.lastName);
);

// A shorter way and cleaner approach...

getUserDetails()
.then (function (userDetails) 
    console.log("firstName: " + userDetails.firstName + ", lastName: " + userDetails.lastName);
);


async function getUserDetails() 
    let user = await JSON.parse(httpGet("https://api.wirespec.dev/wirespec/***/getrandomuserid"));
    let userDetails = await JSON.parse(httpGet("https://api.wirespec.dev/wirespec/***/getuserdetails?id=" + user.id));
    return userDetails;


function httpGet(url) 
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET", url, false);
    xmlHttp.send(null);
    return xmlHttp.responseText;

【讨论】:

【参考方案3】:

您“同时”运行两个 api 调用,因此我们可以假设您永远不会在进行联系人调用时定义 contactId。

所以基本上你想在你有一个contactId后运行联系人呼叫,而不是之前。为此,您可以添加一个额外的效果,该效果将在 contactId 值更改时运行。

另请注意,在您的 sn-p 中,您在联系人呼叫后使用 setActivities 而不是 setContact

这将解决上述问题

const [activities, setActivities] = useState([""]);
const [contact, setContact] = useState();
const currentActivity = activities[0]
let contactId = currentActivity.contacts


useEffect(() => 

    API.getActivities()
      .then(res => 
        setActivities(res.data)
      ).catch((err) => console.log(err))    
, []);

useEffect(() => 
      // Do nothing when contactId is not defined
      if(!contactId) return;
      API.getContact(contactId)
      .then(res => 
        setContact(res.data) // I also modified this line. It was updating the activities in your snippet
      ).catch((err) => console.log(err))

, [contactId]); // This effect will be run every time the contactId changes


console.log(currentActivity)
console.log(contactId)
console.log(contact)

【讨论】:

以上是关于在 UseEffect 中为两个 API 调用设置 Promise的主要内容,如果未能解决你的问题,请参考以下文章

React - 是不是在 useEffect 中调用 API

在 useEffect API 调用之前调用 useState 变量

在 useEffect 中处理 forEach 后调用 setstate

如何阻止多个重新渲染执行多个 api 调用 useEffect?

定期为 Dashboard API 调用运行 useEffect

如何在 React 中使用 useEffect 挂钩调用多个不同的 api