I’m making a React App for my Football team to keep track of Payments and Debts for each Member. I’m trying to search the database for Records for each Season and delete a Member if no records are found. Currently the Member gets deleted then the database searched.
This is the code block (it can also be seen with the rest of my App on GitHub here). I use Firebase to store all my data.
import database from '../firebase/firebase'; export const startRemoveMember = ( playerUuid, seasonList ) => { return (dispatch, getState) => { let canDelete = true; const uid = getState().auth.uid; // This should resolve first seasonList.forEach( (season) => { database.ref(`subs-tracker/users/${uid}/debts_and_payments/${season.seasonUuid}`) .once('value') .then((records) => { records.forEach((childRecord) => { if(childRecord.val().playerUuid === playerUuid) { canDelete = false; return true; // breaks loop if record is found }; }); }); }); // This resolves first before block above if(canDelete) { alert('Deleted'); return database.ref(`subs-tracker/users/${uid}/members/${playerUuid}`) .remove() .then((ref) => { dispatch(removeMember(playerUuid)); // Calls removeMember() function to remove the Member from the State of the App }) } else { alert('Cannot Delete. Member has records'); return false; } }; };
I’m probably missing something basic. I’m no expert with Databases and Async calls. Any help you would be appreciated 🙂
Advertisement
Answer
In your outer forEach
callback you are creating a promise in each iteration. All those promises must resolve first.
You’ll have to await all of them.
So instead of doing forEach
, use map
so you can collect those promises in an array. Then call Promise.all
on those, so you have a promise that will resolve when all those have resolved. Then put the deletion code inside a then
callback. It cannot obviously be executed synchronously.
This also means you have to think about the return value of your (dispatch, getState) =>
function. That function cannot give an indication of success in a synchronous way. So it should best return a promise as well.
const promises = seasonList.map( (season) => database.ref(`subs-tracker/users/${uid}/debts_and_payments/${season.seasonUuid}`) .once('value') .then((records) => records.forEach((childRecord) => // breaks on true childRecord.val().playerUuid === playerUuid ); ); ); return Promise.all(promises).then(findings => findings.includes(true) ).then(cannotDelete => { if (cannotDelete) { alert('Cannot Delete. Member has records'); return false; } else { alert('Deleted'); return database.ref(`subs-tracker/users/${uid}/members/${playerUuid}`) .remove() .then((ref) => { dispatch(removeMember(playerUuid)); // Calls removeMember() function to remove the Member from the State of the App return true; // To distinguish from false }); } });