Problems with async calls inside of loops and setting state

This does not answer my question as it’s not taking into account the looping over async calls, which I suspect is a big part of the problem. I appreciate my explanation might not be the most accurate.

TL;DR: the last setAllSdksInfo(objCopy) won’t work at the end of the code, while console.log just before it will print what should have been set. I believe it’s due to the asynchronous calls I’m making.

I’m working on an app that compare SDKS (their churn and acquisition rates). I have created an API with two separate endpoints.

  1. The first, compares one sdk to another and spits out that data aboth both sdks, how they relate.
  2. The second, simply gets general information about a single sdk.

I’m merging these objects into one, so that all information regarding a single SDK can be found.

PROBLEM: When setting the object at the end with setAllSdksInfo(objCopy) it simply won’t set the object.

I’m getting an ESLint warning which might be related to the problem:

Function declared in a loop contains unsafe references to variable(s) 
'objCopy', 'objCopy', 'objCopy', 'objCopy', 'objCopy', 'objCopy', 'objCopy'
import React, { useEffect } from "react";
import MatrixSquare from "./MatrixSquare";

function Matrix({ selectedSdks, allSdksInfo, setAllSdksInfo, showMatrix, setShowMatrix }) {

  useEffect(() => {
    (async () => {
      const selectedSdksKeys = Object.keys(selectedSdks);
      if (!selectedSdksKeys.length) return;

      setShowMatrix(false);

      let comparedCount = 0;

      const notComparedKeys = [];

      selectedSdksKeys.forEach((key, i) => {
        for (let j = i + 1; j < selectedSdksKeys.length; j++) {
          if ( allSdksInfo?.[key]?.["churned_to"]?.hasOwnProperty( selectedSdksKeys[j])) {
            comparedCount += 1;
            if (comparedCount === 3) {
              setShowMatrix(true);
            }
          } else {
            notComparedKeys.push([key, selectedSdksKeys[j]]);
          }
        }
      });

      let objCopy = Object.assign({}, allSdksInfo);

      for (let i = 0; i < notComparedKeys.length; i++) {
        await fetch( `/api/sdk/churn?sdk1_id=${notComparedKeys[i][0]}&sdk2_id=${notComparedKeys[i][1]}`)
          .then((res) => res.json())
          .then((res) => {
            const copyPreviousObj = Object.assign({}, objCopy);
            objCopy = { ...objCopy, ...res };
            const resKeys = Object.keys(res);

            resKeys.forEach((key) => {
              const new_churned_to = objCopy[key]["churned_to"];
              const new_acquired_from = objCopy[key]["acquired_from"];

              if (copyPreviousObj[key]) {
                objCopy[key]["churned_to"] = {
                  ...new_churned_to,
                  ...copyPreviousObj[key]["churned_to"],
                };
                objCopy[key]["acquired_from"] = {
                  ...new_acquired_from,
                  ...copyPreviousObj[key]["acquired_from"],
                };
              }
            });
          });
      }

      selectedSdksKeys.forEach(async (id, i) => {
        if (allSdksInfo[id]?.["num_installed_apps"]) return;
        const res = await fetch(`/api/sdk/general_info?sdk_id=${id}`);
        const data = await res.json();
        objCopy[id]["num_installed_apps"] = data["num_installed_apps"];
        objCopy[id]["acquired_total"] = data["acquired_total"];
        objCopy[id]["churned_total"] = data["churned_total"];
      });

      console.log(objCopy) // ***************** PRINTS obj OK =)

      setAllSdksInfo(objCopy);
      console.log(allSdksInfo) // ************** PRINTS EMPTY OBJECT =(

      setShowMatrix(true);

    })();
  }, [selectedSdks]);


Source: Ask Javascript Questions

LEAVE A COMMENT