简体   繁体   中英

Promise returning unexpected result

I'm quite new to promises and use state but I have been looking at this for hours and i can't understand what the issue here but it may be my understanding of promises. Any help would be appreciated.

I have a component that is hydrated with props from a response api:

const [trafficData, setTrafficData] = useState();

getTrafficDetails().then(r => {
    setTrafficData(r);
})

<TrafficWidget incomingData={trafficData} />

traffic widget component

const TrafficWidgetComponent = (data) => {
    let [trafficDetails, setTrafficDetails] = useState(null);

    if(data){
      const toShow = data.map((item) => {
        //return some jsx
        return(<div>{item.roadName}: {item.trafficCondition}</div>)
      })

      setTrafficDetails(toShow);
    }

    return(<>{toShow}</>)
}

The method for the api returned in a promise which is consumed by my app is here:

const getTrafficDetails = (linkOne, linkTwo) => {
  const a = fetch(linkOne)
    .then((data) => data.json())
    .then((data) => data.TRAFFIC_INFO);
  const b = fetch(linkTwo)
    .then((data) => data.json())
    .then((data) => data.TRAFFIC_INFO);
  return Promise.all([a, b]).then((td) => {
    const copiedData = [...td]
    //do something with copiedData to combine all objects into array of objects and add new properties
    return combineAndAddMetadata(copiedData)
  });
};

combineAndAddMetadata

const combineAndAddMetadata = (trafficData) => {
  const combinedTrafficObject = [].concat(...trafficData);
  const toReturn = [];

  combinedTrafficObject.map((trafficDataItem) => {
    const { id, cotwoEmissions, utl } = trafficDataItem;

    const filteredObject = toReturn.find((item) => item.id === id);

    if (!filteredObject) {
      trafficDataItem.totalEmission = currencyFormatter(cotwoEmissions * utl);
      toReturn.push(trafficDataItem);
    } else {

      const totalCSL = cotwoEmissions * filteredObject.utl;

      filteredObject.utl += utl;
      filteredObject.totalEmission = totalCSL;
      filteredObject.totalEmissionFormatted = currencyFormatter(totalCSL);
    }
  });

the data that comes back from this method is an array of objects: [{...}, {...}, etc] which I can see in the console.

What I can't understand is why the variable from the callback, the td on the.then in getTrafficDetails is now updated with the new metadata even though I copied it.

Even weirder, when i inspect the data going into the TrafficWidget component from this method, it transforms into an object with the requested data inside it, with strangely with length 0, in the form of {data: [{},{}]} which if i then access in the TrafficWidget component via data.data , causes an infinite loop which causes a crash. (although logging r before setting trafficData shows the correct data in the correct format)

Can anyone shed some light on this?

edit : added response that I receive from the fetch and what i'm trying to end up with

eg data coming from fetch url 1:

[{id: '000', cotwoEmissions: 345, utl: 45},{id: '001', cotwoEmissions: 345, utl: 34}]

eg data coming from fetch url 2:

[{id: '000', cotwoEmissions: 345, utl: 32},{id: '003', cotwoEmissions: 345, utl: 45} ]

what i try and do in the merge is have an array of objects which combine these where the id is the same and where they are the same, the utl values are combined and multiplied by the cotwoEmissions value (which are the same for a given id). I then add some extra properties to each object based on this and return a new array with all of the objects.

so the above, as an output would become:

[{id: '000', cotwoEmissions: 345, utl: 77, totalEmission: 26,565, totalEmissionFormatted: '26,565'},{id: '001', cotwoEmissions: 345, utl: 1, totalEmission: 345, totalEmissionFormatted: '345'}, {id: '003', cotwoEmissions: 100, utl: 2, totalEmission: 200, totalEmissionFormatted: '200'}]

Can't answer for all the strangeness but inside your transformation function, you first destruct the trafficData item but then also modify it. This will modify the td variable as well since it keeps a reference to the same items.

What you likely want is to instead create a new object with the modified values and old values (that you destructured) and add that newly created item into toReturn instead.

This is good practice in general to avoid confusion like this, don't modify input parameters, instead copy and return a new object on each level if something has actually changed. This is mostly for the case of when you don't find the filteredObject (the first time you modify an item), when you find the object you know that the object you found is an object you've created inside this function (assuming you modify your code to create a new object to push into toReturn) so it should be safe to modify again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM