I have the following code:
const imgURL = () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
storageRef.child(`/${fileName}`).getDownloadURL().then((url) => {
console.log(url) // here I am getting the url in the console
return url;
});
}
console.log(imgURL()); // here its undefiend
for some how I dont know why I cant get what the function return
You can try something like this with async / await i don't know if async / await works for react.
const imgURL = async () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
const url = await storageRef.child(`/${fileName}`).getDownloadURL().catch((error) => { throw error });;
return url;
}
change your calling method with this :
console.log(await imgURL());
that what fixed my code (useState)
const [avatarUrl, setAvatarUrl] = useState('');
const imgURL = async () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
await storageRef.child(`/${fileName}`).getDownloadURL().then((url) => {
setAvatarUrl(url);
});
}
imgURL();
Related
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;
};
I am trying to set an object to useSate.
The object is the dataMap which should be passed to the setResults
Any help would be appreciated ?
export default function Home() {
const [results, setResults] = useState([]);
const fetchResults = async () => {
const res = await fetch(
'someurl'
);
const data = await res.json();
const dataMap = data.reduce((acc, curr) => {
return {
...acc,
[curr.id]: curr
};
}, {});
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
return response;
});
const responseAll = await Promise.all(requests);
responseAll.forEach(
({ id, color }) => (dataMap[id] = { ...dataMap[id], color })
);
//console.log(dataMap);
setResults(dataMap)
};
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
return response;
});
This piece of code will trigger all the api's till the end of Object.keys(dataMap)
To update the state with the values.
You need to update the state just after await like this:
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
setResults(prev=>[...prev,response])
});
I have a usecase where I want to call an API at a regular interval until API returns a response saying not to reload further. Following is my code.
const [username, setUserName] = React.useState();
const [reloadEstimates, setReloadEstimates] = React.useState(true);
const [estimates, setEstimates] = React.useState([]);
const [progress, setProgress] = React.useState(true);
const [showError, setShowError] = React.useState(false);
const [errorMessage, setErrorMessage] = React.useState();
useEffect(() => {
const token = jwt(Cookies.get('id-token'));
const username = token["cognito:username"]
setUserName(username)
async function fetchData() {
let allestimates = await fetch(<api-url>, {
//mode: 'cors',
method: 'GET',
headers: new Headers({'Authorization' : 'Bearer ' + Cookies.get('id-token')})
})
.then(res => {
if(res.ok) {
return res.json();
}
throw new Error(`API call failed: ${res.code} - ${res.message}`);
})
.catch(er => { setShowError(true); setErrorMessage(`API error: ${er}`); } );
//console.log(allestimates)
return allestimates;
}
async function refreshEstimate() {
console.log("checkpoint1");
console.log(reloadEstimates);
if (reloadEstimates === true){
fetchData().then(
allelatimates =>{
setEstimates(allelatimates.estimates);
console.log(allelatimates )
console.log(allelatimates.reload )
if(allelatimates.reload === 'NOT RELOAD'){
console.log("checkpoint2");
return false;
}
return true;
}
).then((r)=>{console.log(r);setReloadEstimates(r); console.log(reloadEstimates)}).then(()=>setProgress(false));
}
else{
console.log('No need to fetch.')
}
}
var refreshInterval = setInterval(() => {
refreshEstimate();
}, 10000);
},[]);
I don't know why but setReloadEstimates(r) does not convert state reloadEstimates to false. And it keeps on running. Following is the console output. Please let me know what i am missing or is there another way to achieve this. Thanks.
enter image description here
const sendImageToFirebase = (e) => {
const promises = []
const urlsArray = []
// productimage is an array of image files
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var uploadTask = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random()).put(image);
promises.push(uploadTask.on('state_changed',
(snapshot) => {
},
(error) => {
console.log("error");
},
async () => {
const downloadurl = await uploadTask.snapshot.ref.getDownloadURL()
urlsArray.push(downloadurl)
}
))
})
Promise.all(promises).then(res => {
db.collection("products").doc(idGeneratedforProduct).set(
{
imageURL: urlsArray, //array of image urls
},
).then(e => {
}).catch(error => console.log("Error while sendig items to Firebase"))
})
}
I want to upload a multiple images to firebase storage. Here, sendImagToFirebase is a normal function in reactJs, and productimage is an array of image files. I want to wait for URL for each image files and then store all of them as an array to firestore. I would appreciate your input on how to do it?
You can create a function that receoves the ref and the file and returns the downloadURL. By calling it for each file with a Promise.all you get as result your array of downloadURLs:
const uploadFileAndGetDownloadURL = async (ref, file) => {
const snap = await ref.put(file);
const downloadURL = await snap.ref.getDownloadURL();
return downloadURL;
};
const sendImageToFirebase = async (e) => {
const promises = [];
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var ref = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random());
promises.push(uploadFileAndGetDownloadURL(ref, image));
});
//Your array with the urls
const urlsArray = await Promise.all(promises);
};
I am getting unhandled promise rejection maybe because of ref is not passing please review this if possible. I'll be thankful to you.
[Unhandled promise rejection: TypeError: undefined is not a function (near '...ref.on...')]
In upload file code is distributed based on their functionality:
import firebase from '../../firebase'
const getLocalPath = async (uri)=>{
const response = await fetch(uri);
const blob = await response.blob();
return blob
}
const upload = async (blob, className,acc)=>{
const ref = await firebase.storage().ref(`/${className}`).child(`/${acc}`).put(blob)
console.log('trigger')
console.log('ref', ref)
return ref
};
const getLink = async (ref, className, acc)=>{
await ref.on(
"state_changed",
(snapshot) => {},
(err) => {
console.log(err);
},
() => {
storage
.ref(`/${className}`)
.child(`/${acc}`)
.getDownloadURL()
.then(async (url) => {
// setURL(url)
console.log(url)
return url
});
}
);
}
export {getLink, getLocalPath}
export default upload
This is where I use it:
const Publish= async ()=>{
setLoading(true)
console.log(id)
var count = 1
const blob = await getLocalPath(images[0])
const ref = await upload(blob, `serviceImages/${id}`, `${count}`, setURL)
const uri = await getLink(ref, `serviceImages/${id}`, `${count}` )
console.log(uri)
setLoading(false)
}
firebase.storage.Reference#put returns a firebase.storage.UploadTask. This UploadTask class does have a UploadTask#on() method, BUT, because you used await, you actually get a UploadTaskSnapshot object instead which is the result of UploadTask#then().
So to return the UploadTask instead of the UploadTaskSnapshot, omit the await here.
const upload = (blob, className, acc) => {
return firebase.storage()
.ref(`/${className}`)
.child(`/${acc}`)
.put(blob)
};
But if you want to wait for the upload, just await the UploadTask when you consume the code.
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL); // note no await here
const snapshot = await uploadTask; // wait for upload to finish
const uri = await getLink(uploadTask, `serviceImages/${id}`, `${count}`)
You could also do this:
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL);
const [snapshot, uri] = await Promise.all([
uploadTask,
getLink(uploadTask, `serviceImages/${id}`, `${count}`)
]);
You can also simplify getLink to just:
const getLink = async (uploadTask) => {
return uploadTask
.then((snapshot) => snapshot.ref.getDownloadURL());
}
which leads to:
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL);
const [snapshot, uri] = await Promise.all([
uploadTask,
getLink(uploadTask)
]);
As a side note, try to avoid using await if you are just going to return the value on the next line. It just adds an unnecessary step as you can see here:
const getLocalPath = async (uri)=>{
const response = await fetch(uri);
const blob = await response.blob();
return blob
}
effectively becomes
const getLocalPath = async (uri) => {
return fetch(uri)
.then(response => response.blob())
.then(blob => blob); // <- this line does nothing interesting
}
So just write it as this instead:
const getLocalPath = async (uri) => {
const response = await fetch(uri);
return response.blob();
}