React Native wait for async func - reactjs

I wanna add multiple photo to db by Array.map() and after that add Array with url storage to collection.
I have problem with async function, because i should wait for this function await addImages() but something is not good.
const addImages = async () => {
image.map(async (imagePhoto) => {
const childPath = `post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`;
const response = await fetch(imagePhoto);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
}
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
imageDB.push(snapshot)
})
}
const taskError = snapshot => {
console.log(snapshot)
}
task.on("state_changed", taskProgress, taskError, taskCompleted);
})
}
const addToDbServices = async () => {
await addImages();
firebase.firestore().collection("services")
.doc(firebase.auth().currentUser.uid)
.collection("userServices")
.add({
nameService,
errorCode,
description,
imageDB,
status,
creation: firebase.firestore.FieldValue.serverTimestamp()
}).then(() => {
Alert.alert('Serwis', 'Twoje zgłoszenie zostało pomyślnie dodane'),
navigation.goBack()
})
}

image.map(async (imagePhoto) => {...})
This creates an array of promises. These are executed but not awaited by default, so code execution continues regardless whether the operations are finished or not. If you want to await all these promises you can use Promis.all() like that:
const addImages = async () => {
const pendingOperations = image.map(async (imagePhoto) => {...});
// wait until all images are processed
return Promise.all(pendingOperations); // or await Promise.all(pendingOperations);
}
const addToDbServices = async () => {
await addImages();
...
}

Related

State Not Finished Setting before being used in useEffect

I am hosting a react app in aws amplify using the aws-serverless version of express as the REST API, which sits inside of a lambda function. A big problem that I am facing is that asynchronous jobs in aws-serverless express cause the lambda function to complete before the promises resolve. Leaving me with no data and no error handling. This caused me to bring a lot of the asynchronous work to the front end of the application.
The problem here is that I need to bring a large amount of data into state. Right now, I am using a delay workaround (shown below) but instead need a programatic way to make sure state is finished updating before being used in the second useEffect hook (dependent on odds & failedTries props) instead of using the delay functionality.
Any help would be greatly appreciated.
const App = ({ signOut }) => {
const [odds, setOdds] = useState([]);
const [updateTime,setUpdateTime] = useState(0);
const [failedTries,setFailedTries] = useState(0);
useEffect(() => {
const setNflOdds = async () => {
let response = await updateNflOdds();
let data = response;
setOdds(data);
};
setNflOdds();
setUpdateTime(1);
const interval = setInterval(() => {
setNflOdds();
setUpdateTime(updateTime => updateTime +1);
}, 100000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const s3Push = (() => {
if(!odds.length) {
setFailedTries(failedTries => failedTries + 1);
} else {
const delay = ms => new Promise(res => setTimeout(res, ms));
const nflOddsRefDelay = async() => {
*//This is the current workaround, wait ten seconds before pushing odds state up to the s3 bucket*
await delay(10000);
oddsS3Helper(odds);
};
nflOddsRefDelay()
}
});
s3Push();
}, [odds, failedTries]);
With the above indicated delay workaround this works for my use case (13k records inside of the array) but the data size is highly variable and I want to figure out a way that no matter the data size it brings the entire call up to the s3 bucket.
below is the content of the functions being called in the useEffect hook
const pushToS3 = async ( file, key ) => {
const creds = await Auth.currentCredentials()
const REGION = {region};
const s3Client = new S3Client({
credentials: Auth.essentialCredentials(creds),
region: REGION
});
const params = {
Bucket: {s3 bucket name}
Key: key,
Body: file,
};
s3Client.send(new PutObjectCommand(params));
console.log("file is sent");
};
const oddsS3Helper = (async (odds) => {
console.log("inside s3 helper: ",odds);
let csv = '';
let headers = Object.keys(odds[0]).join(',');
let values = odds.map(odd => Object.values(odd).join(',')).join('\n');
csv += headers + '\n' + values;
const buffedFile = csv;
const key = 'nflprops.csv'
const delay = ms => new Promise(res => setTimeout(res, ms));
const propRefDelay = async() => {
await delay(5000);
await postNflOdds();
};
pushToS3( buffedFile, key );
await propRefDelay();
});
async function getNflGames() {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {} // OPTIONAL
};
const data = await API.get(apiName, path, init);
return data;
};
async function getNflOdds(gameId) {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {}, // OPTIONAL
body: { gameId }
};
const data = await API.post(apiName, path, init);
return data;
};
async function updateNflOdds() {
const ojNflGames = await getNflGames();
const nflGameProps = [];
const nflOddsPush = ( async () => {
try {
await ojNflGames.data.map( async (game) => {
const ojNflOdds = await getNflOdds(game.id)
await ojNflOdds.data[0].odds.map((line) => {
nflGameProps.push(
{
gameId: game.id,
oddsId: line.id,
sports_book_name: line.sports_book_name,
name: line.name,
price: line.price,
checked_date: line.checked_date,
bet_points: line.bet_points,
is_main: line.is_main,
is_live: line.is_live,
market_name: line.market_name,
home_rotation_number: line.home_rotation_number,
away_rotation_number: line.away_rotation_number,
deep_link_url: line.deep_link_url,
player_id: line.player_id,
}
);
});
});
} catch (err) {
console.log("there was an error", err);
}
});
try {
await nflOddsPush();
} catch(err) {
console.log("odds push errored: ", err);
}
console.log("inside of updateNflOdds function: ",nflGameProps);
return nflGameProps;
};

Multiple nested axios calls don't resolve as expected

As described in comments between my code snippet, the asynchronicity is not working as expected. For each id, an object/item should return but it only returns one item since my async await isn't implemented properly. What could be a possible workaround?
Thanks in advance
useEffect(() => {
axios.get('url-here').then((res) => {
res.data.favProperties?.map((el) => {
console.log(el) // this returns multitple id's of saved/liked items
axios.get('url-here').then(async (r) => {
if (r.data) {
console.log(r.data) // Problem starts here
// This returns the full object of the liked items
// But only one object is returned, not every object for which an id was stored
await storageRef
.child(r.data.firebaseRef + '/' + r.data.images[0])
.getDownloadURL()
.then((url) => {
// Here i need to fetch the image for each object
console.log(url)
})
.catch((err) => console.log(err))
}
})
})
})
}, [])
I think breaking down your operations into functions will prevent this Promise Hell. I would recommend using async await for these kinda operations. Also I was confused about the last part of console logging the download URL, by my guess you're trying to save all the download URLs for these liked items in an array.
useEffect(() => {
firstFunction();
}, []);
const firstFunction = async () => {
const { data } = await axios.get("url-here");
const favProperties = data.favProperties;
const fetchedUrls = await Promise.all(
favProperties?.map(async (el) => (
await secondFunction(el.id) /** use el to pass some ID */
))
);
};
const secondFunction = async (someId) => {
/** your second URL must point to some ID (or some parameters) specific API otherwise
running same op in a loop without variations doesn't make any sense */
const { data } = await axios.get(`some-other-url/${someId}`);
if (data) {
console.log(data);
const fetchedUrl = await storageThing(data);
return fetchedUrl;
}
};
const storageThing = async ({ firebaseRef, images }) => {
try {
const downloadURL = await storageRef
.child(firebaseRef + "/" + images[0])
.getDownloadURL();
console.log(downloadURL);
return downloadURL;
} catch (error) {
console.log(error);
return '';
}
};

React Native - state is returning null after setting state

I'm very much new to react native currently i'm building small app for just getting an idea about this. I'm facing an issue in mapping the data from API. This is the json response returning from the api
{"data":[{"digit":300,"countsum":"52"},{"digit":301,"countsum":"102"},{"digit":302,"countsum":"27"},{"digit":303,"countsum":"201"},{"digit":500,"countsum":"101"}]}
When i tried to map this data i'm facing some issues. I stored the response from API to the state and when i tried to display the state data using map function it's showing the state value is null. This the code i tried till now
const [listdata, setListData] = useState(null)
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
}
Do it like this,
export default function ComponentName () {
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>
);
}
You have to wait the fetch execution and later do the list map.
// wait for it
await axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => setListData(response.data))
.catch(error => {
console.log(error);
});
listdata.map(item => <Text>{item.digit}</Text>)
If you want to map the data then do that inside return statement of your code ,like:
return(
{listData?listdata.map(item => return <Text>{item.digit}</Text>):""}
);
This is a sample of a meant in my comment above:
Try console.log listdata at this stage, you will find that it is still
null, in other words, the value of the updated value of the
listdata:useSate will be ready after the render take place. You can
make another function outside of the current one. then use useEffect
with listdata to update your text views
const [listdata, setListData] = useState(null)
useEffect(() => makeRemoteRequest(), [listdata])
makeRemoteRequest = () => {
const url = `your-url-of-data-here`;
fetch(url)
.then(res => res.json())
.then(res => {
setListData(res.data);
})
.catch(error => {
console.log(error)
});
};
You could try the following:
const [listdata, setListData] = useState([])
useEffect(() => {
// Run! Like go get some data from an API.
getListData();
}, []);
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
try {
const dataResponse = await axios.get(constants.BASE_URL + "getlist?token=" +token);
setListData(dataResponse.data || [] );
} catch(error) {
console.log(error);
}
}
return (<>
listdata.map(item => <Text>{item.digit}</Text>)
</>);

Why is async/await not having any effect here?

So I have these two functions - One is an upload function (uploads file to DB). And the other function needs the url's of the uploaded files and for some reason I'm not able to wait for the upload function to finish so I can get the url's of the images uploaded so I can store them in my database.
the problem is at
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
In the following code. Line 12.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return (dispatch, getState, {
getFirebase,
getFirestore
}) => {
const firestore = getFirestore();
//const uid = getState().firebase.auth.uid;
let urls = []
uploadedImages.map(async file => {
urls.push(await uploadedFiles(file, uid, key))
// console.log(urls)
})
return firestore.collection('Uploads').doc(key).set({
uid: uid,
urls: urls,
timestamp: new Date(),
directory: directory
}).then(() => {
dispatch({
type: 'UPLOAD_SUCCESS'
});
toastr.success('הודעה', 'עודכן בהצלחה')
console.log(key)
}).catch(err => {
dispatch({
type: 'UPLOAD_ERROR'
}, err);
toastr.error('אופס! אירעה שגיאה')
});
}
};
Heres the upload function itself:
export const uploadedFiles = (file, uid, key) => {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
return ''
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
return url
})
});
}
A solution would be highly appreciated.
P.S there is no error, its just that the await doesn't await the function to execute.
It simply has no effect.
uploadedFiles should return a promise for your await to work. Await makes javascript to wait until the promise returns with a result. Now your uploadedFiles below resolves url if it successfully gets updated in the database else, it gives out empty string.
Hence, update your uploadedFiles like so...
export const uploadedFiles = (file, uid, key) => {
return new Promise(function(resolve, reject) {
// const firestore = getFirestore();
// const key = firestore.ref().child(property.uid).push().key
// const img = storage.ref().child(property.uid).child(key)
// const uploadedImages = property.uploadedImages
//let uploaded_arr = []
const uploadTask = storage.ref(`Properties/${uid}/${key}/${file.name}`).put(file);
uploadTask.on('state_changed',
(snapshot) => {
// progrss function ....
// const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
//
// console.log(progress);
},
(error) => {
// //error function ....
// dispatch({ type: 'UPLOAD_ERROR'}, error);
toastr.error('הודעת מערכת', error)
reject()
},
() => {
// //complete function ....
storage.ref('Properties').child(String(uid)).child(String(key)).child(file.name).getDownloadURL().then(url => {
// console.log(url)
resolve(url);
})
});
});
}
This is because map will execute all the async lambdas immediately after each other. If you have n element in that array, then map executes n async functions without waiting for the promises being finished.
You can do that using Promise.all. Here the example.
export const handleUploads = (uploadedImages, uid, key, directory) => {
return async (dispatch, getState, { getFirebase, getFirestone }) => {
// ...
const urls = await Promise.all(
uploadedImages.map(async file => uploadedFiles(file, uid, key))
);
// ...
return ...
}
}
Where uploadedFiles needs to be async, too.

Promise not working in React useEffect in combination with Firebase

I want to get data from firebase inside a useEffect function like this:
useEffect(() => {
/** nope */
async function fetchData() {
let dataObject = {};
let dataArray = [];
setAttendees({});
// You can await here
if (newData[listRedux]) {
const request = await Object.keys(newData[listRedux] .
[1].attendees).map(
user => {
usersRef.child(user).on('value', snap => {
dataObject[snap.key] = snap.val();
setAttendees(dataObject);
console.log(dataObject);
let comp = (
<Avatar
key={snap.key}
size="small"
source={snap.val().avatar}
alt={snap.val().name}
/>
);
dataArray.push(comp);
setAttendeesComp(dataArray);
});
}
);
// Wait for all requests, and then setState
await Promise.all(request).then(() => {
console.log('done');
});
}
}
fetchData();
}, [newData, listRedux]);
Now the second console.log inside the promise all will first show then the first console.log, meaning the request was not done yet.
How can i improve my code so the request and the states are first being set and then continue with the rest?
export default function Example() {
const [data, dataSet] = useState(false)
const [attendees, setAttendees] = useState(false)
async function fetchMyAPI() {
let response = await fetch('api/data')
response = await res.json()
console.log(response);
dataSet(response)
}
useEffect(() => {
if (!attendees) return
fetchMyAPI();
}, [attendees, newData, listRedux]);
useEffect(() => {
setAttendees({})
}, [])
More examples here:

Resources