ReactJS onClick function is not triggering correctly - javascript

Function is ONLY triggering correctly if I add e.preventDefault(). However, I want it to push the user to '/' after the user submits the form. Here is the function I am trying to trigger:
onAddPoints = (e) => {
e.preventDefault();
//history.push('/');
let { phoneNumber, points, onPointsChange } = this.state;
//convert string to int -> calculate update points
let p1 = Number(points);
let p2 = Number(onPointsChange);
const updatedPoints = p1 + p2;
//update points
firebase.auth().onAuthStateChanged((user) => {
if(user) {
const docRef = database.collection('users').doc(user.uid).collection('customers').doc(phoneNumber);
docRef.update({
"points": updatedPoints
}).then(() => {
console.log('success');
}).catch((error) => {
console.log(error);
})
} else {
window.location.href = '/';
}
});
}

You need to wait to see the result of your request.
If the request is right then is ok if you send the user to another page but if the request gets an error then you have to tell the user that something is wrong.
firebase.auth().onAuthStateChanged((user) => {
if(user) {
const docRef = database.collection('users').doc(user.uid).collection('customers').doc(phoneNumber);
docRef.update({
"points": updatedPoints
}).then(() => {
history.push('/');
console.log('success');
}).catch((error) => {
console.log(error);
})
} else {
window.location.href = '/';
}
});
And is ok if you use e.preventDefeault() because it stops the default form action flow onSubmit which is go to the page that is setup in the action='./update.php' property. That's commonly used in php apps for example by doing that you send all the inputs info to that update.php.

Related

Node / React - How to send data only to specific users using sse?

I want to send notifications to all and only those users who are following certain items. When user reports item, this triggers a notification of report to all followers of item being reported. What happens in my case:
lets say 3 people follow item xyz. I send notification 'xyz was reported' to 3 users, however, each user gets 3 times that notification instead of 3 users 1x notification.
Do I need to establish a connection with each user seperatly by using his userId in the sse event? Or is there another better solution?
my backend:
const SSE = require("express-sse");
const sse = new SSE(["test"], { isSerialized: false, initialEvent: 'initialize sse' });
...
reportItem: async (req, res) => {
...report item
await NotificationService.notifyOfItemUpdate(id, report.data);
...
}
const NotificationService = {
notifyOfItemUpdate: async (updatedItemId, report) => {
const item = await Item.findById({_id: updatedItemId});
const message = NotificationService.createItemUpdateMessage(item, report);
const followers = await User.find({following: updatedItemId});
await NotificationService.notify(followers, message);
},
notify: async (followers, message) => {
return Promise.all(followers.map(async (follower) => {
const notification = await NotificationService.createNotification(follower._id, message);
if (follower._id.toString() === notification.to.toString()) {
sse.send(notification.messages[0], 'new_notification'); //[0] as sending the last message
}
}));
}
}
client
const eventSource = new EventSource(url, {
headers: {
Authorization: {
toString: function () {
return "Bearer " + token;
},
},
},
});
eventSource.addEventListener('open', (e) => {
console.log('SSE opened!');
});
eventSource.addEventListener(`new_notification${}`, (e) => {
const notifications = JSON.parse(e.data);
dispatch({type: 'STREAM_NOTIFICATIONS_SUCCESS', payload: notifications})
});
eventSource.addEventListener('error', (e) => {
console.error('Error: ', e);
});
return () => {
eventSource.close();
};

Broadcasting to all clients with Deno websocket

I want to add notifications to an application I've developed.
Unfortunately, Deno has removed the ws package.(https://deno.land/std#0.110.0/ws/mod.ts)
That's why I'm using the websocket inside the denon itself. Since it doesn't have many functions, I have to add some things myself.
For example, sending all messages to open clients.
What I want to do is when the pdf is created, a (data, message) comes from the socket and update the notifications on the page according to the incoming data.
I keep all open clients in a Map. and when the pdf is created, I return this Map and send it to all sockets (data, message).
However, this works for one time.
server conf...
import {
path,
paths,
ctid,
} from "../deps.ts";
const users = new Map();
const sockets = new Map()
const userArr = [];
export const startNotif = (socket,req) => {
const claims = req.get("claims");
const org = req.get("org");
claims.org = org;
console.log("connected")
users.set(claims.sub, {"username":claims.sub,"socket":socket})
users.forEach((user)=>{
if(userArr.length === 0){
userArr.push(user)
}
else if(userArr.every((w)=> w.username !== user.username) )
userArr.push(user)
})
sockets.set(org, userArr)
function broadcastMessage(message) {
sockets.get(org).map((u)=>{
console.log(u.socket.readyState)
u.socket.send(message)
})
}
if (socket.readyState === 3) {
sockets.delete(uid)
return
}
const init = (msg) => {
socket.send(
JSON.stringify({
status: "creating",
})
);
};
const ondata = async (msg) => {
const upfilepath = path.join(paths.work, `CT_${msg.sid}_report.pdf`);
try {
const s=await Deno.readTextFile(upfilepath);
if(s){
socket.send(
JSON.stringify({
status: "end",
})
);
} else {
socket.send(
JSON.stringify({
status: "creating",
})
);
}
} catch(e) {
if(e instanceof Deno.errors.NotFound)
console.error('file does not exists');
}
};
const end = () => {
try {
const endTime = Date.now()
const msg = "Your PDF has been created"
const id = ctid(12) // random id create
broadcastMessage(
JSON.stringify({
id: id,
date: endTime,
status: "done",
message: msg,
read: 'negative',
action: 'pdf'
})
);
} catch (e) {
console.log(400, "Cannot send.", e);
}
}
socket.onmessage = async (e) => {
const cmd = JSON.parse(e.data);
if(cmd.bid === 'start'){
await init(cmd)
}
if(!cmd.bid && cmd.sid){
await ondata(cmd)
}
if(cmd.bid === 'end'){
await end();
}
}
socket.onerror = (e) => {
console.log(e);
};
}
client conf...
export const webSocketHandler = (request) =>
new Promise((res, rej) => {
let url;
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
url = `http://localhost:8080/api/notifications/ws`.replace('http', 'ws');
} else {
url = `${window.location.origin}/api/notifications/ws`.replace('http', 'ws');
}
const token = JSON.parse(sessionStorage.getItem('token'));
const orgname = localStorage.getItem('orgname');
const protocol = `${token}_org_${orgname}`;
const socket = new WebSocket(url, protocol);
const response = Object.create({});
socket.onopen = function () {
socket.send(
JSON.stringify({
bid: 'start',
})
);
};
socket.onmessage = function (event) {
response.data = JSON.parse(event.data);
if (response.data.status === 'creating') {
socket.send(
JSON.stringify({
sid: request.sid,
})
);
} else if (response.data.status === 'end') {
socket.send(
JSON.stringify({
bid: 'end',
})
);
} else if (response.data.status === 'done') {
try {
res(response);
} catch (err) {
rej(err);
}
}
};
socket.onclose = function (event) {
response.state = event.returnValue;
};
socket.onerror = function (error) {
rej(error);
};
});
onclick function of button I use in component...
const donwloadReport = async (type) => {
const query = `?sid=${sid}&reportType=${type}`;
const fileName = `CT_${sid}_report.${type}`;
try {
type === 'pdf' && setLoading(true);
const response = await getScanReportAction(query);
const request = {
sid,
};
webSocketHandler(request)
.then((data) => {
console.log(data);
dispatch({
type: 'update',
data: {
id: data.data.id,
date: data.data.date,
message: data.data.message,
action: data.data.action,
read: data.data.read,
},
});
})
.catch((err) => {
console.log(err);
});
if (type === 'html') {
downloadText(response.data, fileName);
} else {
const blobUrl = await readStream(response.data);
setLoading(false);
downloadURL(blobUrl, fileName);
}
} catch (err) {
displayMessage(err.message);
}
};
Everything works perfectly the first time. When I press the download button for the pdf, the socket works, then a data is returned and I update the notification count with the context I applied according to this data.
Later I realized that this works in a single tab. When I open a new client in the side tab, my notification count does not increase. For this, I wanted to keep all sockets in Map and return them all and send a message to each socket separately. But in this case, when I press the download button for the second time, no data comes from the socket.
Actually, I think that I should do the socket initialization process on the client in the context. When you do this, it starts the socket 2 times in a meaningless way.
In summary, consider an application with organizations and users belonging to those organizations. If the clients of A, B, C users belonging to X organization are open at the same time and user A pressed a pdf download button, I want A, B, C users to be notified when the pdf is downloaded.
I would be very grateful if someone could show me a way around this issue.
Have you looked at the BroadcastChannel API? Maybe that could solve your issue. See for example:
Deno specific: https://medium.com/deno-the-complete-reference/broadcast-channel-in-deno-f76a0b8893f5
Web/Browser API: https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

How do I check if the name is already taken in database? React + Axios

I want to make a Validator that validates uniqueness of items. If i want to create new stuff, I want to check if I have it in the database already first.
Code:
const [categList, setCategList] = useState([]);
const getItems = () => {
// Sending HTTP GET request
Axios.get(url).then((response) => {
const categories = [];
response.data.forEach((resCateg) => {
categories.push(resCateg.name + ", " + resCateg.description);
console.log(resCateg);
});
setCategList(categories);
});
};
And before i do:
function submit(e) {
e.preventDefault();
//reset form validation errors
resetFormValidationErrors();
Axios.post(url, {
// Sending HTTP POST request
name: data.name,
description: data.description,
}).then((res) => {
resetForm();
});
}
I want to check under "preventDefault" that my name is unique. Any help?
You just need to store names in your state and then you can check from that array if your name already exist or not.
Here's How to do it-
const [categList, setCategList] = useState([]);
const getItems = () => {
// Sending HTTP GET request
Axios.get(url).then((response) => {
const categoryNames = response.data.map(res => res.name)
setCategList(categoryNames);
});
};
Check while submitting -
function submit(e) {
e.preventDefault();
//reset form validation errors
resetFormValidationErrors();
// Checking here if `categList` already includes name
if(categList.includes(data.name.trim())) {
alert(`${data.name} is already taken, Please select any other name.`)
return
}
Axios.post(url, {
// Sending HTTP POST request
name: data.name,
description: data.description,
}).then((res) => {
resetForm();
});
}

Trying to send params to axios in get - react

I'm new here on the site, and new to React.
I built a function that works great in nodejs. There are rare cases where I want to run this function according to the parameters I send it to, so I try to send it the parameters but I think I can not, I try to print it - and I do not get a print of the parameters I want to send.
i run the function throw click at buttom in react:
<Button onClick={() => {
const result = [1,2,3,4,5,"test"];
props.makeMatchVer2(result);
}}>
make match ver2
</Button>
the action I'm run in axios:
export const makeMatchVer2 = (data) => (dispatch) => {
dispatch({ type: LOADING_DATA });
axios
.get('/kmeans', {
params: {
filterArray: data
}
})
.then((res) => {
dispatch({
type: MAKE_MATCH,
payload: res.data
});
})
.catch((err) => {
dispatch({
type: MAKE_MATCH,
payload: []
});
});
};
the function I'm build in nodeJS:
exports.addUserKmeansMatch = (req, res) => {
console.log("addUserKmeansMatch function start:");
console.log(req.data);
if(req.params)
{
console.log(req.params);
}
let userIndex = 0;
let engineers = [];
let engineersHandles = [];
let engineerDetailsNumeric = {};
db.collection("preferences").get().then(querySnapshot => {
querySnapshot.forEach(doc => {
const engineerDetails = doc.data();
if (engineerDetails.handle === req.user.handle) {
engineersHandles.unshift(engineerDetails.handle);
delete engineerDetails.handle;
engineerDetailsNumeric = convertObjectWithStrToNumber(engineerDetails);
engineers.unshift(engineerDetailsNumeric);
}
else {
engineersHandles.push(engineerDetails.handle);
delete engineerDetails.handle;
engineerDetailsNumeric = convertObjectWithStrToNumber(engineerDetails);
engineers.push(engineerDetailsNumeric);
}
});
kmeans.clusterize(engineers, { k: 4, maxIterations: 5, debug: true }, (err, result) => {
if (err) {
console.error(err);
return res.status(500).json({ error: err.code });
} else {
const cluster = result.clusters;
let foundedMatches = GetUserSerialGroup(userIndex, [...cluster], [...engineers]);
let foundedMatchesHandle = GetUserSerialGroupHandle(userIndex, [...cluster], [...engineersHandles]);
let totalTest = {
foundedMatches: foundedMatches,
foundedMatchesHandle: foundedMatchesHandle,
cluster: cluster,
engineersHandles: engineersHandles,
engineers: engineers
};
let userMatchHandle = reduceUserMatchHandle(foundedMatchesHandle);
userMatchHandle.handle = req.user.handle;
db.doc(`/match/${req.user.handle}`)
.set(userMatchHandle)
.then(() => {
return res.json({ message: "Details added successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
}
})
})
};
Through the button, I send parameters to the function, but I do not see their print, probably something does not work, but I do not know why, I'm new to it
makeMatchVer2 is a thunk. You should call it with dispatch: dispatch(props.makeMatchVer2(result))
The code is correct, I accidentally sent the wrong object, I have 2 objects with almost identical names, one array and the other an object. And I accidentally sent the object instead of the array, it's working right now, thank you.

Using async and await returns parsing error

I have this function that uses validate from yup and it has async function in it.
If I want to use the whole function how can I wait for it to finish
here is code
const handleSubmit = () => {
companyRef.handleProfileFormSubmit();
setModal(true);
setIsSubmitting(true);
console.log(companyRef.handleFocusOnError());
if (!companyRef.handleFocusOnError() && !isButtonValid) {
console.log('first if in handlesubmut', companyRef.handleFocusOnError());
handleBankDetailsClick();
}
if (isButtonValid && !companyRef.handleFocusOnError()) {
bankRef.handleBankFormSubmit();
history.push(DASHBOARD);
} else if (isButtonValid && companyRef.handleFocusOnError()) {
setIsBankDetailsOpen(true);
}
};
I want to wait for the first sentence to finish which is
companyRef.handleProfileFormSubmit();
the async function is here
handleProfileFormSubmit = () => {
const { profile } = this.state;
const { errorValues } = this.state;
let errorExists = false;
let urls = url.format(profile.website.value);
if (!startsWith(urls, 'http://') && !isEmpty(urls)) {
urls = 'http://' + urls;
}
console.log(urls);
this.schema
.validate(
{
name: profile.name.value,
industry: profile.industry.value,
address: profile.address.value,
crn: profile.crn.value,
website: urls,
employeesNbr: profile.employeesNbr.value,
phoneNumber: profile.phoneNumber.value,
userRole: profile.userRole.value,
personCheck: profile.personCheck.value,
},
{ abortEarly: false },
)
.catch(err => {
errorExists = true;
const errors = {};
for (const i of err.inner) {
errors[i.path] = i.message;
}
const sortedErrors = Object.keys(errors);
sortedErrors.forEach(key => {
profile[key].hasError = true;
profile[key].errorMessage = errors[key];
errorValues.inputErrors.value.push(key);
this.setState({ errorValues });
});
})
.then(() => {
console.log('while submitting', errorValues);
this.handleModalError();
if (errorExists) {
this.props.handleCompanyError();
}
});
};
How can I do this?
You're mixing concerns by putting your validation and submit handler into one, but it's still possible (and fine, extract stuff into functions to make it less complicated).
Below example shows where to handle validation errors and submission errors (if submission is async, which it usually is):
handleProfileFormSubmit = async () => {
try {
await this.schema.validate({...});
// now you know your form is valid - move on to submission
try {
await processSubmission(...);
// submission successful!
} catch(err) {
// error occurred while submitting
}
} catch(err) {
// error occurred while validating
}
};

Categories

Resources