Taki
Taki

Reputation: 45

Returned empty object from map function React

I am developing an application using react native by which user can find fishing boat providers from calendar. the user activity flow is the following;

  1. Select fishing methods(can be multiple).
  2. select date from calendar in which number of available fishing boats are shown.

In section number 2, I need to fetch data from firebase realtime database to get data like this.

"2021-01-31": Array [
 Object {
  "endTime": 1612044000000,
  "shipName": "ship nameA",
  "startTime": 1612044000000,
 },
 Object {
  "endTime": 1612044000000,
  "shipName": "ship nameB",
  "startTime": 1612044000000,
 },
],

and code is the following;

const getTourSchedules = async () => {
 const schedules = {};
 await selectedFishingMethods.map((item) => {
  const ref = `schedules/${item}`;
  Firebase.database().ref(ref).on('value', (snapShot) => {
    if (snapShot.exists) {
      snapShot.forEach((docs) => {
        const tour = docs.val().contents;
        const schedule = {
          endTime: tour.endTime,
          startTime: tour.startTime,
          shipName: tour.shipName,
        }
        if (!schedules[tour.date]) {
          schedules[tour.date] = [schedule];
        } else {
          schedules[tour.date].push(schedule);
        }
      })
    }
  })
 });
 console.log(schedules);
 setTours({ ...schedules });
};

The problem is the function above returns empty object and once the page is refreshed, the object is filled up with the data shown above.

Is there any way to wait until the outer map function finishes?

Thank you.

node -V: v14.7.0 expo -V: 3.28.5

Upvotes: 0

Views: 489

Answers (2)

Frank van Puffelen
Frank van Puffelen

Reputation: 599571

Data is loaded from Firebase (and most modern cloud APIs) asynchronously. To allow the user to continue using the app while this happens, your main code continues executing. Then when the data is available, your callback is called.

This means that in your code the setTours({ ...schedules }); executes before schedules[tour.date].push(schedule); is ever called, meaning that you're always setting an empty schedules array to the state, which the app then renders.

Any code that needs data from the database, needs to be inside the callback or be called from there. So in your case, the simplest fix is:

const getTourSchedules = async () => {
 await selectedFishingMethods.map((item) => {
  const ref = `schedules/${item}`;
  Firebase.database().ref(ref).on('value', (snapShot) => {
    const schedules = {};
    if (snapShot.exists) {
      snapShot.forEach((docs) => {
        const tour = docs.val().contents;
        const schedule = {
          endTime: tour.endTime,
          startTime: tour.startTime,
          shipName: tour.shipName,
        }
        if (!schedules[tour.date]) {
          schedules[tour.date] = [schedule];
        } else {
          schedules[tour.date].push(schedule);
        }
      })
    }
    setTours({ ...schedules });
  })
 });
};

Upvotes: 1

Shoaib Ahmed
Shoaib Ahmed

Reputation: 490

It looks calculations (loops) are not finished yet when you setState(...); Try adding await.

getTourSchedules = async () => {
    const schedules = {};
    await selectedFishingMethods.map((item) => {
        const ref = `schedules/${item}`;
        await Firebase.database().ref(ref).on('value', (snapShot) => {
            if (snapShot.exists) {
                snapShot.forEach((docs) => {
                    const tour = docs.val().contents;
                    const schedule = {
                        endTime: tour.endTime,
                        startTime: tour.startTime,
                        shipName: tour.shipName,
                    }
                    if (!schedules[tour.date]) {
                        schedules[tour.date] = [schedule];
                    } else {
                        schedules[tour.date].push(schedule);
                    }
                })
            }
        })
    });
    console.log(schedules);
    setTours({ ...schedules });
};

Upvotes: 1

Related Questions