How to display the message from controller (BE) in toast notification (toastify) - reactjs

Summary:
I am trying to display a message from controller (backend) in notification (toast) using (react-toastify). And then whenever i try to login (cliking the login button) with a wrong e-mail, a toast appears with a message that we can find in the controller file (exemple: 'User with email does not exist' or 'Please enter all fields') ...etc, the message may vary depending on the status or message ofcourse.
My code (Back):
back/controllers/user.js:
login: (req, res) => {
const { email, password } = req.body
if (!email || !password)
return res.status(400).json({ message: 'Please enter all fields' }) <=== this is the message i want to display in toast.
User.findOne({ email }, async (err, user) => {
if (!user) {
res.status(403).json({ message: 'User with email does not exist' }) <=== this is the message i want to display in toast.
console.log('wrongmail');
} else {
const ismatch = await bcrypt.compare(password, user.password)
if (ismatch) {
console.log('ismatch');
const token = signToken(user._id, user.role);
res.cookie("access_token", token, { maxAge: 3600 * 1000, httpOnly: true, sameSite: true });
return res.status(200).json({ isAuthenticated: true, role: user.role })
} else {
res.status(403).json({ message: 'Invalid password !' }) <=== this is the message i want to display in toast.
}
}
})
},
My code (Front):
Front/src/views/Login/index.jsx:
import React, { useState } from 'react'
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
export default () => {
const notify = () => toast(""); <=== What should i put here to be rendred?
return (
<div className="col-lg-6">
<fieldset>
<input value={email} onChange={(e) => setemail(e.target.value)} type="text" name="email" id="email" pattern="[^ #]*#[^ #]*" placeholder="Your Email" required />
</fieldset>
<fieldset>
<input value={password} onChange={(e) => setpassword(e.target.value)} type="password" name="password" id="subject" placeholder="password" autoComplete="on" />
</fieldset>
<fieldset>
<button type="submit" id="form-submit" className="main-button" onClick={notify}>Login</button>
<ToastContainer />
</fieldset>
</div>
)
}
I added the gist code in my Login/index.jsx that i found it here: https://fkhadra.github.io/react-toastify/installation, but i can't manage to find a way to call any of messages from controller file (BE) to render as i explained at first in summary.

You should use the toast method inside the function where you get the response from BE.
for example:
getUserDetails() {
//call backend here
try {
const data = result_from_backend
toast.success('your toast message')
} catch (e) {
toast.error('show the error here');
}
}

Related

i want to show details on same page

i am developing an application i.e supply chain management application on reactJS, NodeJS and blockchain.
Frontend code:
import React, { Component } from 'react'
import { useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from 'react-router-dom';
const SignUp = () => {
const navigate = useNavigate();
const flag=0;
const [data, setData] = useState({
uname: "",
email: "",
location: "",
budget: "",
password: ""
});
const handleChange = (e) => {
const value = e.target.value;
setData({
...data,
[e.target.name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
const userData = {
uname: data.uname,
email: data.email,
location: data.location,
budget: data.budget,
password: data.password
};
axios
.post("http://localhost:8080/api/signup/", userData)
.then((response) => {
console.log(response);
})
.catch((error) => {
if (error.response) {
console.log(error.response);
console.log("server responded");
} else if (error.request) {
console.log("network error");
} else {
console.log(error);
}
});
navigate(`/home`)
};
return (
<form>
<h3>Sign Up</h3>
<div className="mb-3">
<label>User Name</label>
<input
type="text"
name="uname"
value={data.uname}
className="form-control"
placeholder="User name"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<label>Email address</label>
<input
type="email"
name="email"
value={data.email}
className="form-control"
placeholder="Enter email"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<label>Location</label>
<input
type="text"
name="location"
value={data.location}
className="form-control"
placeholder="Location"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<label>Budget</label>
<input
type="Number"
name="budget"
value={data.budget}
className="form-control"
placeholder="Budget"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<label>Password</label>
<input
type="password"
name="password"
value={data.password}
className="form-control"
placeholder="Enter password"
onChange={handleChange}
/>
</div>
<div className="d-grid">
<button type="submit" onClick={handleSubmit}className="btn btn-primary">
Sign Up
</button>
</div>
<p className="forgot-password text-right">
Already registered sign in?
</p>
</form>
);
};
export default SignUp;
here if user successfully registered then i want to show deatils of the user on the same page. how should i do that?
i have attached the code and the screenshot of the page.
currently i am on my account page.
Inside of your handle submit
You can just navigate after the axios.then callback
Or if you want the behavior to be that user submits -> register success -> show success -> then redirect, you can setTimeout for say 1000ms and then navigate.
axios
.post("http://localhost:8080/api/signup/", userData)
.then((response) => {
console.log(response);
})
.then(() => {
setTimeout(() => navigate(`/home`), 1000);
}
.catch((error) => {
if (error.response) {
console.log(error.response);
console.log("server responded");
} else if (error.request) {
console.log("network error");
} else {
console.log(error);
}
});
If you mean, show the user data after a successful registration and assuming you're calling an api to register the user and you're getting the user details back on success, you can handle that in your handleSubmit method.
Here's an example
const showUserDetails = (userDetails) => {
// Code that shows user details
// Probably using state
};
const handleSubmit = (e) => {
e.preventDefault();
const userData = {
...
axios
.post("http://localhost:8080/api/signup/", userData)
.then((response) => {
// handle here
showUserDetails(response);
})
.catch((error) => {
if (error.response) {
...
} else {
console.log(error);
}
});
};

Why won't the form state visibly change within the submit button using react form hook?

So I have a sign up form using react-hook-form and I want to make the submit input disabled and display a "Signing in..." message. I've console logged the isSubmitting value within the render and that shows true when I submit and then false not long after however the submit button within the form never updates to reflect the isSubmitting status.
What am I doing wrong? Here is the React Hook Form useFormState docs
From what I can see it should work?
Thanks in advance.
import { useState } from "react"
import { useForm, useFormState } from "react-hook-form"
import useAuth from "Hooks/useAuth"
const SignInForm = () => {
const [firebaseError, setFirebaseError] = useState(null)
const { signIn } = useAuth()
const {
register,
handleSubmit,
resetField,
control,
formState: { errors },
} = useForm()
const { isSubmitting, isValidating } = useFormState({ control })
const onSubmit = (data) => {
signIn(data.email, data.password)
.then((response) => console.log(response))
.catch((error) => {
let message = null
if (error.code === "auth/too-many-requests") {
message =
"Too many unsuccessful attempts, please reset password or try again later"
}
if (error.code === "auth/wrong-password") {
message = "Incorrect password, please try again"
}
if (error.code === "auth/user-not-found") {
message = "User does not exist, please try again"
}
resetField("password")
setFirebaseError(message)
})
}
return (
<form
className="signupForm"
onSubmit={handleSubmit(onSubmit)}
autoComplete="off"
>
{console.log(isSubmitting)}
{firebaseError && (
<p className="form-top-error has-text-danger">{firebaseError}</p>
)}
<div className="field">
<input
type="text"
className="input formInput"
placeholder="Email"
{...register("email", {
required: {
value: true,
message: "Field can not be empty",
},
pattern: {
value:
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
message: "Invalid email",
},
})}
/>
{errors.email && (
<span className="is-block has-text-danger is-size-7">
{errors.email?.message}
</span>
)}
</div>
<div className="field">
<input
type="password"
className="input formInput"
placeholder="Password"
{...register("password", {
required: "Field can not be empty",
minLength: {
value: 6,
message: "Must be longer than 6 characters",
},
})}
/>
{errors.password && (
<span className="is-block has-text-danger is-size-7">
{errors.password?.message}
</span>
)}
</div>
<input
type="submit"
className="button is-info"
value={isSubmitting ? "Signing In..." : "Sign In"}
disabled={isSubmitting}
/>
</form>
)
}
export default SignInForm
I think you need to refactor your onSubmit function to make it async so isSubmitting will stay true during your signIn call.
const onSubmit = async (data) => {
await signIn(data.email, data.password)
.then((response) => console.log(response))
.catch((error) => {
let message = null
if (error.code === "auth/too-many-requests") {
message =
"Too many unsuccessful attempts, please reset password or try again later"
}
if (error.code === "auth/wrong-password") {
message = "Incorrect password, please try again"
}
if (error.code === "auth/user-not-found") {
message = "User does not exist, please try again"
}
resetField("password")
setFirebaseError(message)
})
}
onSubmit needs to return a Promise for formState to update correctly.
const onSubmit = (payload) => {
// You need to return a promise.
return new Promise((resolve) => {
setTimeout(() => resolve(), 1000);
});
};
References:
https://react-hook-form.com/api/useform/formstate/
https://github.com/react-hook-form/react-hook-form/issues/1363#issuecomment-610681167

Why does my Nodemailer POST request work in postman but not from my react front-end?

I am using the Nodemailer package and a SendinBlue smtp email server to create a website contact form. When I send a request using postman the email sends and everything works as it should, however, when I try an Axios post request from my react front-end (in both production and live environments) I get the following:
POST http:///send 404 (Not Found)
My Server.js file:
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3001;
const nodemailer = require('nodemailer')
require('dotenv').config();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, 'build')));
}
// Catch all route to redirect unmanaged routes back to the react app.
// THIS OPTION IS NOT SEO OPTIMIZED
// THIS ROUTE SENDS ALL SAVED LINKS TO THE ROOT
app.get('/*', function (req, res) {
res.redirect('/')
});
// CONNECTION NOT YET TESTED
// Nodemailer config to handle contact form submit.
let transporter = nodemailer.createTransport({
host: "smtp-relay.sendinblue.com",
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
// Post route for contact form submission
app.post('/send', (req, res) => {
console.log(req.body)
var { name, email, message } = req.body;
let mailOptions = {
from: `"${name}" <${email}>`, //'"Fred Foo 👻" <foo#example.com>', sender address
to: `${process.env.EMAIL}`, // list of receivers
subject: "Atlas Pet Contact Form",
text: `${message}`, // plain text body
}
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log('Error: ', err)
} else {
console.log('Email Sent')
}
});
res.json('Email Sent');
});
app.listen(PORT, () => { console.log(`API Server now listening on PORT ${PORT}!`) })
React contact component:
import React, { useState } from "react";
import axios from "axios";
import "../../pages/style.css";
function ContactForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
message: ""
});
function sendEmail(e) {
e.preventDefault();
axios.post("/send", formData)
.then(res => {
console.log(res)
console.log(res.data)
})
.catch(err => {
console.log(err)
})
};
function handleNameChange(e) {
setFormData({ ...formData, name: e.target.value })
};
function handleEmailChange(e) {
setFormData({ ...formData, email: e.target.value })
};
function handleMessageChange(e) {
setFormData({ ...formData, message: e.target.value })
};
return (
<div className="jumbotron form-spacing">
<form className="form">
<h1 className="display-4 main-txt"><strong>Drop a Line</strong></h1>
<br/>
<div className="hr"></div>
<br/>
<p className="lead main-txt">Questions, Comments, Concerns? Let us know!</p>
{/* Name input */}
<label htmlFor="name">Name</label>
<input
className="inputs"
type="text"
id="name"
name="name"
onChange={handleNameChange}
></input>
<br />
{/* Email input */}
<label htmlFor="email">Email</label>
<input
className="inputs"
type="email"
id="email"
name="email"
onChange={handleEmailChange}
></input>
<br />
{/* Message input */}
<label htmlFor="message">Message</label>
<textarea
className="inputs"
rows="6"
id="message"
name="message"
onChange={handleMessageChange}
></textarea>
{/* Submit button */}
<button
className="submit-btn btn btn-light btn-submit"
type="submit"
value="Send Message"
onClick={sendEmail}
>Send Message</button>
</form>
<div className="row-overlay"></div>
</div>
)
}
export default ContactForm;

react-hook-form handling server-side errors in handleSubmit

I'm having a hard time figuring out how to handle errors that don't necessarily pertain to a single input field in a react-hook-form.
To put it differently, how do I handle handleSubmit errors?
For example, having the following form:
import to from 'await-to-js'
import axios, { AxiosResponse } from 'axios'
import React from "react"
import { useForm } from "react-hook-form"
type LoginFormData = {
username: string,
password: string,
}
export const Login: React.FC = () => {
const { register, handleSubmit } = useForm<LoginFormData>()
const onSubmit = handleSubmit(async (data) => {
const url = '/auth/local'
const [err, userLoginResult] = await to<AxiosResponse>(axios.post(url, data))
if (userLoginResult) {
alert('Login successful')
}
else if (err) {
alert('Bad username or password')
}
})
return (
<div className="RegisterOrLogIn">
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">username</label>
<input name="username" id="username" ref={register} />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" ref={register} />
</div>
<button type="submit"> </button>
</form>
</div>
)
}
Is there a react-hook-form way of informing the user that there's an error with either the username or the password?
as in, other than alert()
Perhaps this is answered elsewhere, but I could not find it.
Clarification
The error received from the server does not pertain to a single field:
{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"messages": [
{
"id": "Auth.form.error.invalid",
"message": "Identifier or password invalid."
}
]
}
],
"data": [
{
"messages": [
{
"id": "Auth.form.error.invalid",
"message": "Identifier or password invalid."
}
]
}
]
}
In order to display the error from the server to your user, you need to use:
setError to set the error programmatically when the server returns an error response.
errors to get the error state of every fields in your form to display to the user.
type FormInputs = {
username: string;
};
const { setError, formState: { errors } } = useForm<FormInputs>();
In your handleSubmit callback
axios
.post(url, data)
.then((response) => {
alert("Login successful");
})
.catch((e) => {
const errors = e.response.data;
if (errors.username) {
setError('username', {
type: "server",
message: 'Something went wrong with username',
});
}
if (errors.password) {
setError('password', {
type: "server",
message: 'Something went wrong with password',
});
}
});
In your component
<label htmlFor="username">username</label>
<input id="username" {...register("username")} />
<div>{errors.username && errors.username.message}</div>
Live Demo
Inspired by #NearHuscarl's answer, I've done the following hack s.t. changes in either the username or the password inputs will remove the single error.
This hack does not scale well if your error is related to multiple fields in the form, but it worked for the login use case.
onSubmit:
const onSubmit = handleSubmit(async (data) => {
const url = '/auth/local'
const [err, userLoginResult] = await to<AxiosResponse>(axios.post(url, data)) // see await-to-js
if (userLoginResult) {
alert('Login successful')
}
else if (err) {
const formError = { type: "server", message: "Username or Password Incorrect" }
// set same error in both:
setError('password', formError)
setError('username', formError)
}
})
component:
return (
<div className="RegisterOrLogIn">
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">username</label>
<input name="username" id="username" ref={register} />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" ref={register} />
</div>
<div>{errors.username && errors.password?.message /*note the cross check*/}</div>
<button type="submit"> </button>
</form>
</div>
)
by setting and rendering the error on both errors.password & errors.username, the error will disappear when the user updates either of those fields.

Redirect React Router Route From onClick in Modal

Below is code for the body of a sign up modal. How I would like it to work would be the user enters credentials and when they click submit they are automatically taken to the route "/secret" which is their dashboard.
Currently, when the user clicks submit an error is thrown and the alert "('Error logging in please try again')" appears. I only want this to happen if there is an error with their input (i.e. nonunique entry, not hitting requirements ect.)
This alert is thrown every time the user clicks submit regardless if the input hits requirements and is pushed to the database. Also for some reason react seems to over look the line of code to redirect the user to the desired route.
if (res.status === 200)
{ this.props.history.push('/secret');
}
Is there a way to redirect the route if I receive a res.status === 200 while also keeping the condition to throw the error if it occurs?
signup.js (modal body)
import React, { Component } from 'react';
import API from "../utils/API.js";
import { Container, Row, Col, Button, Alert, Modal, ModalHeader, ModalBody, ModalFooter, Label, Input } from 'reactstrap';
export default class Signup extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
username: ''
};
}
handleInputChange = (event) => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
onSubmit = (event) => {
event.preventDefault();
API.signUpUser(this.state.email, this.state.username, this.state.password)
.then(res => {
if (res.status === 200) {
this.props.history.push('/secret');
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render() {
console.log(this.state)
return (
<form onSubmit={this.onSubmit}>
Email: <input
type="email"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Username: <input
type="text"
name="username"
placeholder="Enter username"
value={this.state.username}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Password: <input
type="password"
name="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
<Button type="submit" value="Submit" color="primary" className="btn btn-warning">Sign Up</Button>
</form>
);
}
}
app.js (react routes)
<Switch>
<Route path="/" exact component={Home} />
<Route path="/hometwo" exact component={Home2} />
<Route path="/secret" component={withAuth(Secret)} />
<Route path="/login" component={Login} />
<Route path="/signup" component={Signup} />
<Route path="/signout" component={Signout} />
<Route path="/loggedIn" component={withAuth(loggedIn)} />
</Switch>
Signup User API
signUpUser: function (email, username, password) {
const signUpURL = "/api/user"
// console.log("username " + username + "password " + password)
return axios.post(signUpURL, { email,username, password }).then(result => result.data);
// return axios.post(signUpURL, { email, username, password }).then(result => result.data);
// .then(result => result.data);
},
NEW Signup.js
import React, { Component } from 'react';
import API from "../utils/API.js";
import { Container, Row, Col, Button, Alert, Modal, ModalHeader, ModalBody, ModalFooter, Label, Input } from 'reactstrap';
export default class Signup extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
username: '',
};
}
handleInputChange = (event) => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
onSubmit = (event) => {
event.preventDefault();
// API.signUpUser(this.state.email, this.state.username, this.state.password)
fetch('/api/authenticatesignup', {
method: 'POST',
body: JSON.stringify(this.state),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
console.log("THIS IS res " + JSON.stringify(res.status));
if (res.status === 200) {
// alert("You can now go to your dashboard")
// window.location.reload();
// // alert("You can now go to your dashboard")
this.props.history.push('/secret')
} else {
const error = new Error(res.error);
// console.log("This is error on client side " + error)
throw error;
// alert('Error logging in please try again');
}
})
.catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render() {
console.log(this.state)
return (
<form onSubmit={this.onSubmit}>
Email: <input
type="email"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Username: <input
type="text"
name="username"
placeholder="Enter username"
value={this.state.username}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Password: <input
type="password"
name="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
<Button type="submit" value="Submit" color="primary" className="btn btn-warning">Sign Up</Button>
</form>
);
}
}
API endpoint of signup in server
//authenticate checker signup
app.post('/api/authenticatesignup', function (req, res) {
const { email, username, password } = req.body;
User.create({
email: req.body.email,
username: req.body.username,
password: req.body.password
},
function (err, user) {
if (err) {
console.error(err);
res.status(500)
.json({
error: 'Internal error please try again'
});
}
else {
// Issue token
const payload = { username };
const token = jwt.sign(payload, secret, {
expiresIn: '1h'
});
res.cookie('token', token, { httpOnly: true })
.sendStatus(200);
console.log("New USER!" + req.body.email + req.body.password)
}
})
});
You need to wrap the component with withRouter.
You can get access to the history object’s properties and the closest 's match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
import React, { Component } from 'react';
import { withRouter } from "react-router";
import API from "../utils/API.js";
import { Container, Row, Col, Button, Alert, Modal, ModalHeader, ModalBody, ModalFooter, Label, Input } from 'reactstrap';
class SignupComp extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
username: '',
};
}
handleInputChange = (event) => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
onSubmit = (event) => {
event.preventDefault();
// API.signUpUser(this.state.email, this.state.username, this.state.password)
fetch('/api/authenticatesignup', {
method: 'POST',
body: JSON.stringify(this.state),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
console.log("THIS IS res " + JSON.stringify(res.status));
if (res.status === 200) {
// alert("You can now go to your dashboard")
// window.location.reload();
// // alert("You can now go to your dashboard")
this.props.history.push('/secret')
} else {
const error = new Error(res.error);
// console.log("This is error on client side " + error)
throw error;
// alert('Error logging in please try again');
}
})
.catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render() {
console.log(this.state)
return (
<form onSubmit={this.onSubmit}>
Email: <input
type="email"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Username: <input
type="text"
name="username"
placeholder="Enter username"
value={this.state.username}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
Password: <input
type="password"
name="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handleInputChange}
required
/>
<br></br>
<br></br>
<Button type="submit" value="Submit" color="primary" className="btn btn-warning">Sign Up</Button>
</form>
);
}
}
const Signup = withRouter(SignupComp);
export default Signup;

Resources