action is triggering a series of remote methods and returning rejected - reactjs

I have an action that is getting dispatched that is triggering multiple remote methods and returning'TALK_SUBMIT_REJECTED' The strange thing however, is that all of the data that I am getting is still returned and updating the store as expected.
I am however getting these two errors in the process:
xhr.js:178 POST http://localhost:3000/api/talks/talkSubmit 500
(Internal Server Error)
createError.js:16 Uncaught (in promise) Error: Request failed with
status code 500
at e.exports (createError.js:16)
at e.exports (settle.js:18)
at XMLHttpRequest.m.(:3000/anonymous function)
(http://localhost:3000/bundle.js:6:2169)
I have thrown in two dozen console.logs recording all of the data I am sending and receiving and everything returning as expected.
I apologize in advance for the long post but I have been struggling with this bug for a while.
to give a brief summery of what my code is doing:
I have a form that upon submission, triggers an action that starts a chain of remote methods.
This is the first method:
function talkSubmit(speakerInfo, talkInfo, date) {
return new Promise((resolve, reject) => {
const { Talk, Speaker, Event } = app.models;
return Speaker.create(speakerInfo)
.then(response => {
let speakerId = response.id
return getMeetups()
.then(meetups => {
const index = meetups.findIndex((item) =>
item.date == date);
let name = meetups[index].name;
let details = meetups[index].description;
let meetupId = meetups[index].meetupId;
if (index === -1)
return reject(new Error('NO meetup with that
date found!'));
return Event.findOrCreate({ date, name, details,
meetupId })
.then(event => {
let eventId = event[0].id
return Talk.create({ ...talkInfo,
speakerId, eventId })
.then(talk => resolve(talk))
.catch(err => console.log(err))
})
.catch(err => reject(err))
})
.catch(err => reject(err))
})
.catch(err => reject(err))
})
}
module.exports = { talkSubmit };
//this is the get meetups function that is called by talkSubmit
function getMeetups() {
return new Promise((resolve, reject) => {
let currentDate = new Date();
currentDate.setMonth(currentDate.getMonth() + 3);
const date ='${ currentDate.getFullYear() } -${
currentDate.getMonth() } -${
currentDate.
getDay()
} ';
axios.get(`https://api.meetup.com/sandiegojs/events?
no_later_than = ${ date } `)
return resolve(response.data.map(event => ({
meetupId: event.id,
name: event.name,
date: event.local_date,
time: event.local_time,
link: event.link,
description: event.description,
})))
.catch(err => reject(new Error('getMeetups failed to get SDJS
meetups')))
})
}
module.exports = { getMeetups };
//This is the after remote method that is triggered when talkSubmit is
//completed.
Talk.afterRemote('talkSubmit', function (ctx, modelInstance, next) {
const speakerId = ctx.result.speakerId;
const eventId = ctx.result.eventId;
const approved = false;
const pending = true;
formatTalkForEmail(speakerId, eventId)
.then((response) => {
const speakerName = response.speakerName;
const speakerEmail = response.speakerEmail;
const meetupTitle = response.meetupTitle;
const meetupDate = response.meetupDate;
sendEmailToSpeaker(process.env.ADMIN_EMAIL, approved,
pending, speakerEmail, speakerName, meetupTitle, meetupDate)
.then(() => next())
.catch(err => next(err));
})
.catch(err => next(err));
});
//this is the formatTalkForEmail method called in the remote method
function formatTalkForEmail(speakerId, eventId) {
return new Promise((resolve, reject) => {
if (speakerId == undefined) {
return reject(new Error('speakerId is undefined'));
}
if (eventId == undefined) {
return reject(new Error('eventId is undefined'));
}
const { Speaker, Event } = app.models;
Speaker.findById(speakerId)
.then(speaker => {
const speakerName = speaker.speakerName;
const speakerEmail = speaker.speakerEmail
return Event.findById(eventId)
.then(selectedEvent => {
const meetupTitle = selectedEvent.name;
const meetupDate = selectedEvent.date;
resolve({
speakerName,
speakerEmail,
meetupTitle,
meetupDate
})
})
.catch(err => reject( err ))
})
.catch(err => reject(err))
})
}
module.exports = { formatTalkForEmail };
//and finally this is the sendEmailToSpeaker method:
function sendEmailToSpeaker(adminEmail, approved, pending,
speakerEmail, speakerName, meetupTitle, meetupDate) {
return new Promise((resolve, reject) => {
let emailContent;
if (approved && !pending) {
emailContent = `Congratulations! Your request to speak at
${ meetupTitle } on ${ meetupDate } has been approved.`
}
if (!approved && !pending) {
emailContent = `We're sorry your request to speak at
${ meetupTitle } on ${ meetupDate } has been denied.`
}
if (pending) {
emailContent = `Thank you for signing up to speak
${ meetupTitle } on ${ meetupDate }.You will be notified as soon as
a
SDJS admin reviews your request.`
sendEmailToAdmin(adminEmail, meetupDate, meetupTitle,
speakerEmail, speakerName)
.catch(err => console.log(err));
}
const email = {
to: speakerEmail,
from: adminEmail,
subject: 'SDJS Meetup Speaker Request',
templateId: process.env.ADMIN_SPEAKER_EMAIL_TEMPLATE,
dynamic_template_data: {
emailContent: emailContent,
sdjsBtn: false,
title: 'SDJS Meetup Speaker Request'
}
}
sgMail.send(email)
.then(() => resolve({ email }))
.catch(err => {
console.log(err);
reject(err);
});
})
}
in conclusion I have no clue what part of talkSubmit is throwing those two errors and yet both emails are getting automatically sent and the store is updating with all the proper data despite the initial action creator returning rejected. I appreciate any help anyone can offer.

Ok so I think the main problem is here:
axios.get(`https://api.meetup.com/sandiegojs/events?no_later_than = ${ date } `)
return resolve(response.data.map(event => ({
meetupId: event.id,
name: event.name,
date: event.local_date,
time: event.local_time,
link: event.link,
description: event.description,
})))
it should look like that (I recommend to extract mapping method):
axios.get(`https://api.meetup.com/sandiegojs/events?no_later_than = ${ date} `)
.then( response => resolve(response.data.map(event => ({
meetupId: event.id,
name: event.name,
date: event.local_date,
time: event.local_time,
link: event.link,
description: event.description,
}))))
.catch(err => reject(new Error('getMeetups failed to get SDJS meetups')))
and a few words about Promises. You can chain them like that:
method()
.then(method2())
.then(method3())
.then(method4())
...
.catch(err =>...
also I don't understand why you wrap content of method in return new Promise((resolve, reject) => { I think is not needed in your case
so you can change talkSubmit method to something like that (this is just a draft, I recommend to extract methods from then blocks)
function talkSubmit(speakerInfo, talkInfo, date) {
const { Talk, Speaker, Event } = app.models;
return getMeetups()
.then(meetups => {
const index = meetups.findIndex((item) => item.date == date);
let name = meetups[index].name;
let details = meetups[index].description;
let meetupId = meetups[index].meetupId;
if (index === -1)
return reject(new Error('NO meetup with that date found!'));
return Promise.all([
Event.findOrCreate({
date, name, details,
meetupId
}),
Speaker.create(speakerInfo)])
})
.then(([event, speaker]) => {
let eventId = event[0].id
let speakerId = speaker.id
return Talk.create({
...talkInfo,
speakerId, eventId
})
})
}

Related

Asynchronous Function In NextAuth Completing Before Value Returned

On signin, I'm attempting to query FirestoreDB and then return the user data or null.
I'm trying to do this with async await, but the dependent code runs before the db query has been completed. This can be seen when "CORRECT," (right credentials) is console logged after the empty user details (not complete).
Thanks,
This is my code:
let data = await db.collection('users').where("email", "==", email).get().then(querySnapshot => {
console.log("SOMETHING")
let arr = []
querySnapshot.docs.map(doc => arr.push(doc.data()))
console.log(arr)
// console.log(sc.decrypt(arr[0].password))
if (arr[0].email == email) {
bcrypt.compare(password, arr[0].password, (err, match) => {
if (match) {
console.log("CORRECT")
return arr[0]
} else {
if (err) {
console.log(err)
}
console.log("INCORRECT")
return null
}
})
}
})
console.log("DATA " + data)
return data ? data : null
An hour of searching, but 2 minutes after I post I figure it out.
Here is the working code for anyone in the same boat:
let data = new Promise((resolve, reject) => {
db.collection('users').where("email", "==", email).get().then(querySnapshot => {
console.log("SOMETHING")
let arr = []
querySnapshot.docs.map(doc => arr.push(doc.data()))
console.log(arr)
// console.log(sc.decrypt(arr[0].password))
if (arr[0].email == email) {
bcrypt.compare(password, arr[0].password, (err, match) => {
if (match) {
console.log("CORRECT")
resolve(arr[0])
} else {
if (err) {
console.log(err)
}
console.log("INCORRECT")
resolve(null)
}
})
}
})
})
Promise.all([data]).then(async () => {
console.log(await data)
})
return data ? data : null

How to send a notification instead of a message when user is offline using socket.io

So whenever another user is offline and an online user tries to send a message, it returns an error. The error is caused because it didn't find a socket where the user is in, but I can console.log something from the error response, since I put it in if/else block. So i was wondering how could I send a response to the client and push a notification for the user who is offline. The explanation is probably confusing, so here is the code:
index.js(socket):
const io = require('socket.io')(8900, {
cors: {
origin: 'http://localhost:3000',
},
});
let users = [];
const addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
const removeUser = (socketId) => {
users = users.filter((user) => user.socketId !== socketId);
};
const getUser = (userId) => {
return foundUser = users.find((user) => user.userId === userId)
};
io.on('connection', (socket) => {
//when ceonnect
console.log('a user connected.');
//take userId and socketId from user
socket.on('addUser', (userId) => {
addUser(userId, socket.id);
io.emit('getUsers', users);
});
//send and get message
socket.on('sendMessage', ({ senderId, recieverId, text }) => {
const user = getUser(recieverId);
console.log('RECIEVERRRR', user);
if (user) {
io.to(user.socketId).emit('getMessage', {
senderId,
text,
});
} else {
// IMPORTANT: This is where I can console.log the error, but can't figure out how to send the response to the client
}
});
//when disconnect
socket.on('disconnect', () => {
console.log('a user disconnected!');
removeUser(socket.id);
io.emit('getUsers', users);
});
});
dashboard.js (I cut out most of the code, but this is where i'm sending request to socket):
const handleSubmit = async (e) => {
e.preventDefault();
const message = {
sender: user._id,
text: newMessage,
conversationId: currentChat._id,
};
const recieverId = currentChat.members.find(
(member) => member !== user._id
);
console.log('REC from FRONT END', recieverId);
socket.current.emit('sendMessage', {
senderId: user._id,
recieverId,
text: newMessage,
});
try {
const { data } = await axios.post('/api/messages/addMessage', message);
setMessages([...messages, data]);
setNewMessage('');
} catch (err) {
console.log(err);
}
};
Any help would be appreciated. Thanks.
You can emit a new event sendMessageFailed:
//send and get message
socket.on('sendMessage', ({ senderId, recieverId, text }) => {
const user = getUser(recieverId);
console.log('RECIEVERRRR', user);
if (user) {
io.to(user.socketId).emit('getMessage', {
senderId,
text,
});
} else {
io.to(user.socketId).emit('sendMessageFailed', "Your friend is offline");
}
});
At the client side listen to the event:
socket.current.on('sendMessageFailed', (err) => {
//Sending message has failed!
});

Socket works the for a few seconds then fails

I've managed to establish a connection using socket. It works great for the first few seconds after that it runs super slow takes like almost 2-3 mins to complete a request. And then it produces this error continuously. The app doesn't crash it just runs slowly with the error displaying countless times.
Firefox can’t establish a connection to the server at ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=9S6kqHJdHHXQgrobAAHp..
Error on console.
Main.js
function Home(props) {
const [Username , setUsername] = useState("")
const [SearchedData, setSearchedData] = useState()
const [Data, setData] = useState()
const socket = io('http://localhost:5000')
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
})
function NavBar(props){
const handleClick = (e) => {
const {id} = e.target
if(id === "Post-btn"){
if(Content.length > 0){
let data = {
Username: "yada", Content
}
props.socket.emit("store-data", data)
}
}
return(
Tags....
)}
function Content (props) {
const onLike = (e) => {
const { id } = e.target.dataset
const data = {
username: "yada",
id : id
}
// console.log(data)
props.socket.emit("like", data)
}
return(
Tags.....
)
}
server.js
mongoose.connect(process.env.MongoDB,
{ useNewUrlParser: true, useUnifiedTopology: true }).then(() => {
console.log("Database Connected")
}).catch(err => {
console.log(err)
});
const server = app.listen(process.env.Port, () => {
console.log("Connected on " + process.env.Port)
})
const io = socket(server, {
cors:{
origin: "http://localhost:3000",
credential: true,
}
})
let cuid;
io.on("connection", (socket) => {
socket.on("content", username => {
Comments.find({},(err, data) => {
if(!err)
socket.emit("get-data", data)
})
})
socket.on("store-data", data => {
const {Username, Content} = data
const newdata = new Comments({
userName: Username,
content: Content,
createdAt: new Date().toDateString(),
replies: []
})
newdata.save().then(data => {
for(const d in data)
if(d === "_id"){
Users.findOneAndUpdate({username: Username}, {$push: {UserContent: data[d]}}, {new: true}, (err, save) => {
if(err)
console.log(err)
else
console.log(save)
})
}
})
})
socket.on("like", data => {
const {username, id} = data
Users.findOne({username:username}, (err, data) => {
if(!err){
cuid = data['id']
console.log(cuid)
Comments.findByIdAndUpdate(id, {$set: {score: data['_id']}}, {upsert: true}, (err, d) => {
if(!err){
console.log(d)
}
})
}
})
})
})
Looking at the code provided, I noticed there is an useEffect without params. This may be causing a loop until the application crashes.
React.useEffect(() => {
// socket.current = io('http://localhost:5000')
socket.emit("content","yada")
socket.on("get-data", data => {
setData(data)
})
socket.on("Updated", data => {
setData(data)
})
}, []); <- this is missing
This empty array indicates that the content inside the useEffect will only run once.
More about this https://reactjs.org/docs/hooks-intro.html

Wait for get request response before continuing with a post request in axios

I have tried to use async/await in the createSwap function but it did not work for me. Before creating a request, I want to run the getSlotDetails function to set the slotDets so that it can be used in the post request below. However, the createSwap function does not wait for the getSlotDetails function to complete before sending the post request. How do I fix this?
const [slotDets, setSlotDets] = useState([]);
const getSlotDetails = (moduleCode, slotId, slotType) => {
axios
.get(`https://api.nusmods.com/v2/2020-2021/modules/${moduleCode}.json`)
.then((response) => response.data)
.then((data) => data.semesterData[0])
.then((semesterData) => semesterData.timetable)
.then((timetable) =>
timetable.filter(
(slot) => slot.classNo === slotId && slot.lessonType == slotType
)
)
.then((result) => {
setSlotDets(result);
});
};
const createSwap = (
moduleCode,
slotType,
currentSlot,
desiredSlots,
completed,
reserved
) => {
dispatch(createRequest());
getSlotDetails(moduleCode, currentSlot, slotType);
axios
.post(
"http://localhost:3001/api/v1/swaps",
{
module_code: moduleCode,
slot_type: slotType,
current_slot: currentSlot,
desired_slots: desiredSlots,
completed: completed,
reserved: reserved,
venue: slotDets.venue,
startTime: slotDets.startTime,
endTime: slotDets.endTime,
day: slotDets.day,
},
{
headers,
}
)
.then((response) => {
console.log(response.data);
dispatch(createSuccess());
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
})
.catch((error) => {
console.log(error.response.data);
dispatch(createFail(error.response.data));
setTimeout(() => {
dispatch(resetSwap());
}, 2000);
});
};
const makeTwoRequests = async () => {
try{
const firstResponse = await axios.get(FIRST_URL);
if(firstResponse.status === 200){
const firstResponseData = firstResponse.data;
const secondResponse = await axios.get(SECOND_URL);
if(secondResponse.status === 200){
const secondResponseData = secondResponse.data;
}
}
}
catch(error){
console.log(error)
}
}
maketwoRequests()
firstResponseData and secondResponseData can be state variables instead of const for better access throughout the Component

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