Chaining async/await calls in react - reactjs

I have an application that can add NYT bestseller items to a database. Currently, users can add a bestseller to the db even if it already exists in the db. I want to be able to chain API calls such that if a user tries to save an item, the application first checks if that item is in the db, and only if it isn't proceed to save the item.
Here is my existing code:
const [currentInDb, setCurrentInDb] = useState(false);
interface bookInt {
title: string;
author: string;
}
const handleDbCheck = async(book: bookInt) => {
setCurrentInDb(false);
let targetObj = {
title: book.title,
author: book.author,
list: selectedCategory
}
try {
let url = baseURL + "/read-all";
axios.get(url).then((res) => {
for (let i = 0; i < res.data.length; i++){
let current = res.data[i]
if (current.title === targetObj.title && current.list === targetObj.list){
setCurrentInDb(true);
}
}
});
} catch (error) {
console.log(error);
}
}
const handleSaveBook = async (book: bookInt) => {
if (currentInDb){
console.log('handleSaveBook stopped early because item in db');
return;
}
try {
let newObj = {
title: book.title,
author: book.author,
list: selectedCategory,
};
let postURL = baseURL + "/create";
axios.post(postURL, newObj).then((response) => {
console.log('new item added');
});
} catch (error) {
console.log("error: ", error);
}
};
const handleCheckAndSave = async(book: bookInt): Promise<any> => {
await handleDbCheck(book)
.then(res => handleSaveBook(book))
}
Oddly, upon page reload, the first time I try to add an item to the db that is already there, I CAN add a duplicate. Then if I try to add it again, it correctly does not allow me to add it. Ideas?

There is no need to use .then in the async function. you can simply use await & chain your asynchornous requests.
const [currentInDb, setCurrentInDb] = useState(false);
interface bookInt {
title: string;
author: string;
}
const handleDbCheck = async(book: bookInt) => {
setCurrentInDb(false);
let targetObj = {
title: book.title,
author: book.author,
list: selectedCategory
}
try {
let url = baseURL + "/read-all";
const res = await axios.get(url)
for (let i = 0; i < res.data.length; i++){
let current = res.data[i]
if (current.title === targetObj.title && current.list === targetObj.list){
setCurrentInDb(true);
}
}
} catch (error) {
console.log(error);
}
}
const handleSaveBook = async (book: bookInt) => {
if (currentInDb){
console.log('handleSaveBook stopped early because item in db');
return;
}
try {
let newObj = {
title: book.title,
author: book.author,
list: selectedCategory,
};
let postURL = baseURL + "/create";
const response = await axios.post(postURL, newObj)
console.log(response)
} catch (error) {
console.log("error: ", error);
}
};
const handleCheckAndSave = async(book: bookInt): Promise<any> => {
await handleDbCheck(book)
await handleSaveBook(book)
}

Related

Why is this private class member out of scope at the point where I try to call it?

I want to be able to call "setUser", but it's out of scope for some reason. This is in a MobX store that I've created. I'm sure it's something I'm doing fundamentally wrong, but I don't know what it is. Here's the code:
private setUser = (user: UserType) => {
this.userRegistry.set(user.Username, user);
}
loadUsersFromPoolGroups = () => {
this.loadingInitial = true;
try {
var congitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const USER_POOL_ID = 'us-east-2_kWOEamV6i';
var params = {
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listGroups(params, function(err, data) {
if (err) console.log(err, err.stack);
else {
data.Groups.forEach(group => {
var params = {
GroupName: group.GroupName,
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listUsersInGroup(params, function(err1, data1) {
if (err1) console.log(err1, err1.stack);
else {
data1.Users.forEach((user) => {
this.setUser(user);
})
}
})
})
}
});
} catch (error) {
console.log('error loading users from pool groups', error)
}
}
I'm doing a similar thing in a different store with no issues.
private setSubmittal = (submittal: Submittal) => {
this.submittalRegistry.set(submittal.submittalId, submittal);
}
loadSubmittals = async () => {
this.loadingInitial = true;
try {
const submittals = await agent.Submittals.list();
submittals.data.forEach((submittal: Submittal) => {
this.setSubmittal(submittal);
})
this.setLoadingInitial(false);
} catch (error) {
console.log(error);
this.setLoadingInitial(false);
}
}
I expected to be able to call setUser and it won't let me.

Discord.js voice, player on finish event

I'm trying to learn discord.js so I wanted to learn how music bots work. The code below is my shot and everything works but I'm having an issue with my queue.
I cannot get the player.on "finish" or "end" events. So when the song ends I want to call the recursive method that plays a song but I don't know how to get the correct event.
I have tried using player.on(AudioPlayerStatus.Idle) events and making something out of that but that didn't work out so well.
If anyone has any advice to what am I doing wrong or how to improve the code below, I'd be grateful.
const queue = new Map();
module.exports = {
name: "play",
aliases: ["skip", "stop", "leave"],
async execute(message, args, cmd, client, Discord) {
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) {
return message.channel.send({
content: "You need to be in a channel to execute this command!",
});
}
const permissions = voiceChannel.permissionsFor(message.member);
if (!(permissions.has('CONNECT')) || !(permissions.has('SPEAK'))) {
return message.channel.send({
content: "You don't have the correct permissions!",
});
}
const serverQueue = queue.get(message.guild.id);
if (cmd == "play") {
if (!(args.length)) {
return message.channel.send({
content: "Invalid song link!",
});
}
let song = {};
if (ytdl.validateURL(args[0])) {
const songInfo = await ytdl.getInfo(args[0]);
song = { title: songInfo.videoDetails.title, url: songInfo.videoDetails.video_url }
} else {
const videoFinder = async (query) => {
const videoResult = await ytSearch(query);
return (videoResult.videos.length > 1) ? videoResult.videos[0] : null;
}
const video = await videoFinder(args.join(" "));
if (video) {
song = { title: video.title, url: video.url }
} else {
message.channel.send({
content: `No video results found!`
})
}
}
if (!(serverQueue)) {
const queueConstructor = {
voiceChannel: voiceChannel,
textChannel: message.channel,
connection: null,
player: null,
songs: [],
}
queue.set(message.guild.id, queueConstructor);
queueConstructor.songs.push(song);
try {
const connection = await joinVoiceChannel({
channelId: message.member.voice.channel.id,
guildId: message.member.voice.channel.guild.id,
adapterCreator: message.member.voice.channel.guild.voiceAdapterCreator,
});
queueConstructor.connection = connection;
const player = createAudioPlayer({
behaviors: {
noSubscriber: NoSubscriberBehavior.Pause
}
});
queueConstructor.player = player;
videoPlayer(message.guild, queueConstructor.songs[0]);
} catch (error) {
queue.delete(message.guild.id);
message.channel.send({
content: `There was an error connecting!`
})
throw error;
}
} else {
serverQueue.songs.push(song);
console.log(serverQueue.songs);
return message.channel.send({
content: `${song.title} added to the queue!`
})
}
} else if (cmd == "skip") {
// TODO
} else if (cmd == "leave") {
// TODO
}
}
}
const videoPlayer = async (guild, song) => {
const songQueue = queue.get(guild.id);
if (!(song)) {
songQueue.player.stop();
songQueue.connection.destroy();
queue.delete(guild.id);
return;
}
const stream = await ytdl(song.url, { filter: "audioonly" });
const songStream = await (createAudioResource(stream));
const subscription = songQueue.connection.subscribe(songQueue.player);
const dispatcher = songQueue.player.play(createAudioResource(stream)).on("finish", () => {
songQueue.songs.shift();
songQueue.playing = false;
videoPlayer(guild, songQueue.songs[0]);
songQueue.textChannel.send({
content: `Now playing: ${song.title}`
});
});
}
player.addListener("stateChange", (oldOne, newOne) => {
if (newOne.status == "idle") {
console.log("The song finished");
}
});
Use event stateChange. It called when voice state change from oldOne to newOne. (ex: playing -> idle)
newOne { status: "playing | idle | buffering | ..." }

Next.js how send 200 status to client in getInitialProps

I need to transfer status from the server to the client if I receive status 200 without content for getMainPage request. How can i do this?
I tried (example from google):
if (ctx.res) ctx.res.statusCode = 404;
return {notFound: true};
ctx.res always = undefined
/main page.ts/
IndexPage.getInitialProps = async (ctx: IExtendedAppContext): Promise<IPageProps> => {
const { reduxStore } = ctx;
const regionId = reduxStore.getState().regions.current?.id;
const cityId = reduxStore.getState().regions.current?.city;
const transaction = apm?.startTransaction('IndexPage');
const main: IMain = await reduxStore.dispatch(getMainPage({ region: regionId, city: cityId }, transaction));
const span = startSpan('fetchAlphabetList', transaction);
const alphabetList = await alphabetListService.fetch({ region: regionId, city: cityId })
.finally(() => endSpan(span));
endTransaction(transaction);
return { pageMeta: main.page_meta, alphabetList };
};
/with-redux-store.tsx/
export type Store = ReturnType<typeof getOrCreateStore>;
interface IProps {
reduxStore: Store;
initialReduxState: Store;
}
export interface IExtendedAppContext extends NextPageContext {
reduxStore: Store;
}
export interface IInitialProps extends AppContext {
ctx: IExtendedAppContext;
}
getMainPage request and all get requests uses that get method
public async get(entity: string, query: object, pathVariables: string[] | number[] = [], cookies: string = '') {
const queryURI = makeURIParams(query);
const key = makeQueryKey(entity, query, pathVariables);
try {
const localCopy = await this.getLocalCopy(key);
return this.handleResponse(localCopy);
} catch (error) {
console.log(this.getUrlAPI(entity, queryURI, pathVariables));
return this.fetch(this.getUrlAPI(entity, queryURI, pathVariables), {headers: {...this.getCookies(cookies)}})
.then(this._httpHandler).then(async (dataJSON: any) => {
try {
const { meta = {} } = dataJSON;
meta.requestDate = getCurrentTime();
const { expire, date } = meta;
if (expire <= date) {
await this.purgeStorageByKey(key);
return dataJSON;
}
if (expire !== 0) await this.setLocalCopy(key, JSON.stringify(dataJSON));
return dataJSON;
} catch (error) {
console.log(this.getUrlAPI(entity, queryURI, pathVariables), error);
return null;
}
}).then(this.handleResponse).catch((error: Error) => {
console.log(this.getUrlAPI(entity, queryURI, pathVariables), error);
return null;
});
}
}
/method where we can get request's status/
private _httpHandler(response: Response): Promise<IResponse | null> {
return new Promise(async (resolve, reject) => {
if ((response.status >= 200 && response.status < 300) || response.status === 403) {
try {
const json = await response.json();
resolve({ requestUrl: response.url, responseHeaders: response?.headers, ...json });
} catch (_) {
resolve(null);
}
} else {
reject(response.statusText);
}
});
}
so if it is async function and returns value, you can check status,
let mainResponseStatus = false;
if (main.status === 200) {
mainResponseStatus = true;
}
and then continue your code and return whatever you want but defense it in return
return {
somethingToReturn: mainResponseStatus ? returnWhatYouWant : []
}

Cancelling Axios get request in React

I have an Axios get request I'd like to cancel upon an event but it doesn't seem to work.
// user.services.js
searchFAQS(query) {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
source.cancel('Operation cancelled by user')
return axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: source.token,
params: {
query: query
}
}
)
}
// ClassComponent
onChangeQuery = (e) => {
if (e.target.value === "") {
this.setState({
fFaq: "",
query: e.target.value
})
} else {
UserServices.searchFAQS().cancel()
this.setState({query: e.target.value},
() => UserServices.searchFAQS(this.state.query)
.then(resp => {
this.setState({
fFaq: resp.data,
fGroups: resp.data.map(f => f.group).filter((value, index, self) => self.indexOf(value) === index)
})
}))
}
}
I read the cancellation part for the Axios documentation, which is what led me to the attempt above, but it doesn't seem to be canceling after observing the requests from developer tools.
searchFAQS(query) {
const CancelToken = axios.CancelToken;
.....
new CancelToken is creating on every searchFAQS call, so it will not get cancel because everytime it's a new token
change as below
let token = null; // define cancel token outside the search fn, then it will not recreate on every call
searchFAQS(query) {
if (token !== null) {
token();
}
...
const { CancelToken } = axios;
...
return axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: new CancelToken(function executor(cancellableFn) {
token = cancellableFn;
}),
params: {
query: query
}
}
....
On my understanding you solution should looks like this:
// user.services.js
async searchFAQS(query, source = '') {
const search = axios.get(
authHeader.getApiUrl() + '/api/v1/search_faqs',
{
cancelToken: source.token,
params: {
query: query
}
}
);
if (source /* change to your needs, actualy it cancels all requests */) {
source.cancel('Ok, its just canceled!');
}
return await search.data;
}
// ClassComponent
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
onChangeQuery = (e) => {
if (e.target.value === "") {
this.setState({
fFaq: "",
query: e.target.value
})
} else {
UserServices.searchFAQS("", source)
this.setState({query: e.target.value},
() => UserServices.searchFAQS(this.state.query, source)
.then(resp => {
this.setState({
fFaq: resp.data,
fGroups: resp.data.map(f => f.group).filter((value, index, self) => self.indexOf(value) === index)
})
}))
}
}

How to make Async Await Function in React Native?

I want to create a function that is about uploading photo to Firebase Storage with react-native-fetch-blob. I'm using Redux and you can find action functions below:
My problem is that uploadImage function is not running like asynchronous. Firebase function is running before uploadImage, so application give me an error.
I think i can't make a asynchronous function. How can i fix it ?
uploadImage() function:
const uploadImage = async (imageSource, whereToUpload) => {
let imageURL = '';
const mime = 'image/jpg';
const { Blob } = RNFetchBlob.polyfill;
const { fs } = RNFetchBlob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
console.log('URI =>', imageSource.uri);
let imgUri = imageSource.uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref(whereToUpload + '/' + imageSource.fileName);
const uploadUri = Platform.OS === 'ios' ? imgUri.replace('file://', '') : imgUri;
await fs.readFile(uploadUri, 'base64')
.then((data) => Blob.build(data, { type: `${mime};BASE64` }))
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
// eslint-disable-next-line no-return-assign
return imageURL = imageRef.getDownloadURL();
})
.catch((error) => {
console.log(error);
});
return imageURL;
};
and the main action is:
export const addProjectGroup = (
myUser,
groupName,
groupDescription,
groupProfilePic,
) => dispatch => {
const groupProfileFinalPic = async () => {
let finalGroupPicture = { landscape: '' };
if (_.isEmpty(groupProfilePic.src)) {
await uploadImage(groupProfilePic, 'groupPictures').then((imageURL) => {
console.log('İŞLEM TAMAM!');
console.log('SELECTED IMAGE URL =>', imageURL);
finalGroupPicture.landscape = imageURL;
});
} else {
finalGroupPicture.landscape = groupProfilePic.src.landscape;
}
return finalGroupPicture;
};
console.log("final group profile pic =>", groupProfileFinalPic());
// Önce grubu yaratalım..
// eslint-disable-next-line prefer-destructuring
const key = firebase
.database()
.ref()
.child('groups')
.push().key;
firebase
.database()
.ref('/groups/' + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid,
},
groupName,
groupDescription,
groupProfilePic: groupProfileFinalPic(),
projects: '',
})
.then(() => {
console.log('Groups oluşturuldu.');
})
.catch(e => {
Alert.alert('Hata', 'Beklenmedik bir hata meydana geldi.');
console.log(e.message);
});
dispatch({
type: ADD_PROJECT_GROUP,
});
};
You are not awaiting groupProfileFinalPic(). This should be done before creating the action you want to dispatch.
groupProfileFinalPic().then(groupProfilePic => {
return firebase
.database()
.ref("/groups/" + key)
.set({
admin: {
email: myUser.email,
name: myUser.name,
uid: myUser.uid
},
groupName,
groupDescription,
groupProfilePic,
projects: ""
})
.then(() => {
console.log("Groups oluşturuldu.");
})
.catch(e => {
Alert.alert("Hata", "Beklenmedik bir hata meydana geldi.");
console.log(e.message);
});
});
I have no clue what the last dispatch is for, you might want to do that in one of the callbacks. Your code is to verbose for an SO question, but I hope this helps anyways.
You are using both await and then on the same call. To use await, you can arrange it something like
const uploadImage = async (imageSource, whereToUpload) => {
...
try {
let data = await RNFS.fs.readFile(uploadUri, 'base64')
let uploadBlob = await Blob.build(data, { type: `${mime};BASE64` }))
...etc...
return finalResult
catch (e) {
// handle error
}
}

Resources