How to handle error and success response the cleanest way? - reactjs

How would I show a server response error or success message the cleanest way ?
Right now, I'm using an async function to make an axios request, and on success/error im just updating a local state (with react-hook-form), but I feel like it's "ugly" and I want the pages to be as clean as possible and put the code to handle success and error messages in the service request, behind the scenes.
Example :
ForgotPassword.jsx
import React, { useState } from 'react';
import Layout from '../components/core/Layout';
import axios from 'axios';
import { useForm } from 'react-hook-form';
import { Button, Form, Alert } from 'react-bootstrap';
import { regex } from '../constants';
import { isAuth } from '../helpers';
import { forgotPassword } from '../services/User';
import { Redirect } from 'react-router-dom';
const Forgot = () => {
const {
handleSubmit,
register,
errors,
getValues,
setError,
setValue,
clearError
} = useForm({
mode: 'onBlur'
});
register({ name: 'responseError' });
register({ name: 'responseSuccess' });
const { responseSuccess } = getValues();
const onSubmit = async values => {
try {
const response = await forgotPassword(values);
setValue([{ responseSuccess: response.data.message }]);
// set response success msg to local state responseSuccess
} catch (error) {
setError('responseError', '', error);
// set response error msg to local state responseError
}
};
const forgotPasswordForm = () => (
<>
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group>
<Form.Label>Email address</Form.Label>
<Form.Control
name='email'
ref={register({
required: true,
pattern: {
value: regex.email,
message: 'Invalid email address'
}
})}
type='email'
placeholder='Enter email'
isInvalid={errors.email}
/>
<Form.Control.Feedback type={errors.email ? 'invalid' : 'valid'}>
{errors.email && errors.email.message}
</Form.Control.Feedback>
</Form.Group>
<Button variant='primary' type='submit'>
Submit
</Button>
</Form>
<br />
{errors.responseError && (
<Alert
variant='danger'
dismissible
onClose={() => clearError('responseError')}>
{errors.responseError.message}
</Alert>
)}
</>
);
const forgotPasswordFormSuccess = () => (
<Alert
variant='success'
className='mt-5'
dismissible
onClose={() => setValue([{ responseSuccess: '' }])}>
{responseSuccess}
</Alert>
);
if (isAuth()) return <Redirect to='/' />;
return (
<Layout>
<div>
<h1>Forgot password</h1>
{responseSuccess ? forgotPasswordFormSuccess() : forgotPasswordForm()}
</div>
</Layout>
);
};
export default Forgot;
forgotPassword Function
export const forgotPassword = async ({ email }) => {
return new Promise(async (resolve, reject) => {
try {
const response = await Axios({
method: 'PUT',
url: `${process.env.REACT_APP_API}/forgot-password`,
data: { email }
});
resolve(response);
} catch (error) {
if (error.response) {
reject(error.response && error.response.data.error);
}
reject('Something went wrong. please try again later.');
}
});
};

Hope this is what you want
export const forgotPassword = ({ email }) => {
return new Promise((resolve, reject) => {
axios(`${process.env.REACT_APP_API}/forgot-password`, {
method: 'PUT',
data: { email }
})
.then(res => resolve(res.data))
.catch(err => reject(err))
});
};
const onSubmit = values => {
forgotPassword(values)
.then(res => setValue([{ responseSuccess: res.message }]))
.catch(err => setError('responseError', '', err));
};

Related

Weird CORS issue with login in React?

I have a small issue here. Basically I am trying to work on a login/register system built in React.
This is how I handle the signup:
const submitHandler = async (data: object) => {
console.log(data);
await fetch("http://localhost:4000/signup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then((data) => {
if (data.status_code === "SUCCESS") {
router.push("/login");
}
})
.catch((err) => console.log(err));
};
That works perfectly fine and also saves the data in the database after signing up, but my login has some issues not handling the user (redirecting him)
const submitHandler = async (data: object) => {
await fetch("http://localhost:4000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then((data) => {
if (data.status_code === "SUCCESS") {
localStorage.setItem("userData", JSON.stringify(data.data));
router.push("/dashboard");
} else {
setError("Invalid Credentials!");
}
})
.catch((err) => console.log(err));
};
When I enter the correct data from the signup, nothing really happens (it should set the jwt token into the localstorage and then redirect me to the dashboard route), any ideas?
This is not redirecting you to login route because if you clearly look at bottom of your signup request handler where you are comparing data.status_code === " SUCCESS" that should be data.status_code === "SUCCESS".
"SUCCESS" & " SUCCESS" both are different.
For me it seems like that you have a typo when you check the data.status_code. You misspelled " SUCCESS" with "SUCCESS". If that was the case, then you can move on, but here is an alternate solution you could use. I also provided a back-end file (I used Node.js with Express).
import React, { useState } from 'react';
import PropTypes from 'prop-types';
async function submitHandler(credentials) {
return fetch('http://localhost:4000/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
export default function Login({ setToken }) {
const [username, setUserName] = useState();
const [password, setPassword] = useState();
const handleSubmit = async e => {
e.preventDefault();
const token = await submitHandler({
username,
password
});
setToken(token);
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<form onSubmit={handleSubmit}>
<label>
<p>Username</p>
<input type="text" onChange={e => setUserName(e.target.value)} />
</label>
<label>
<p>Password</p>
<input type="password" onChange={e => setPassword(e.target.value)} />
</label>
<div>
<button type="submit">Submit</button>
</div>
</form>
</div>
)
}
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
useToken.js - contains the logic for the custom hook
import { useState } from 'react';
export default function useToken() {
const getToken = () => {
const tokenString = localStorage.getItem('token');
const userToken = JSON.parse(tokenString);
return userToken?.token
};
const [token, setToken] = useState(getToken());
const saveToken = userToken => {
localStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
};
return {
setToken: saveToken,
token
}
}
App.js
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import './App.css';
import Dashboard from '../Dashboard/Dashboard';
import Login from '../Login/Login';
import Preferences from '../Preferences/Preferences';
import useToken from './useToken';
function App() {
const { token, setToken } = useToken();
if(!token) {
return <Login setToken={setToken} />
}
return (
<div className="wrapper">
<h1>Application</h1>
<BrowserRouter>
<Switch>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/preferences">
<Preferences />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
server.js
const express = require('express');
const cors = require('cors')
const app = express();
app.use(cors());
app.use('/login', (req, res) => {
res.send({
token: 'test123'
});
});
app.listen(8080, () => console.log('API is running on http://localhost:4000/login'));

Heroku not running like it should after trying to post data i get a json object loading instead of a page

I'm having problems with heroku. After i try to post data (post a listing), instead of being redirected to listings page where you would find a listing I just posted (that's how it works when i run it locally), the page displays a json object. And refresh doesn't work, i need to type the address again and then everything works normally, and the listing that i just posted is there as it should.
This is the front end
import { useState } from 'react'
import axios from 'axios'
import { useNavigate } from 'react-router-dom'
const PostListings = () => {
let navigate = useNavigate()
const [ newList, setNewListing ] = useState({
city: '',
neighborhood: '',
bedrooms: '',
price: '',
img: '',
reviews_id: []
})
const getNewListing = async () => {
console.log(newList)
await axios({
url: `${window.location.origin}/listings`,
method: 'post',
data: newList
})
}
const handleChange = (e) => {
setNewListing({...newList, [e.target.name]: e.target.value })
console.log(e.target.name)
console.log(e.target.value)
console.log(newList)
}
const handleSubmit= () => {
getNewListing()
navigate('/listings')
window.location.reload(false)
}
return (
<div>
<h2>Add A New Listing</h2>
<form className="submit-form" onSubmit={handleSubmit}>
<input type="text" value={newList.city} onChange={handleChange} name={'city'} placeholder={'city'} />
<input type="text" value={newList.neighborhood} onChange={handleChange} name={'neighborhood'} placeholder={'neighborhood'} />
<input type="text" value={newList.img} onChange={ handleChange} name={'img'} placeholder={'image'} />
<input type="text" value={newList.price} onChange={ handleChange} name={'price'} placeholder={'price'} />
<input type="text" value={newList.bedrooms} onChange={ handleChange} name={'bedrooms'} placeholder={'bedrooms'} />
<button>Submit</button>
</form>
</div>
)
}
export default PostListings
//this is in Controllers
const postListing = async (req, res) => {
try {
console.log('data:', req.body)
const listing = await new Listing(req.body)
console.log('new:', listing)
await listing.save()
return res.status(201).json({ listing })
} catch (error) {
return res.status(500).json({ error: error.message })
}
}
//and in index.js
app.post('/listings', listingsController.postListing)

React - Component rendering multiple times

I'm using functional components and within that components I'm using useState so when I redirect to that component and log some text so it printed almost thousand times and this is very frustrating for me please help.
import React, { useState, useEffect } from 'react';
import Modal from 'react-bootstrap/Modal'
import Form from 'react-bootstrap/Form'
import Button from 'react-bootstrap/Button'
import DatePicker from 'react-date-picker';
import axios from 'axios';
import useToken from '../../../account/useToken';
import TokenInfo from '../../../account/TokenInfo'
function CreateOrUpdateEvent(props) {
const { token, setToken } = useToken();
const [value, onChange] = useState(new Date());
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const {userId} = TokenInfo();
console.log("Add Event Rendered");
if(props.id != undefined)
{
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}
const setEventName = (name) => {
setName(name);
}
const setEventDescription = (description) => {
setDescription(description);
}
const setEventDate = (eventDate) => {
var eventDate = new Date(eventDate);
onChange(eventDate);
}
const saveEvent = () => {
var event = {
Id: props.id,
Name: name,
Description: description,
EventDate: value,
IsDeleted: false,
CreatedDateTime: new Date(),
UserId: userId
}
const headers = {
'Authorization': `bearer ${token}`
}
axios.post('https://localhost:5001/Event/CreateOrUpdate', event, {
headers: headers
})
.then(function () {
props.onHide();
})
.catch(function (error) {
console.log(error);
});
}
return (
<>
<Modal
{...props}>
<Modal.Header>
<Modal.Title>Create Event</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group controlId="formBasicName">
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Name" value={props.id != undefined ? name : ''} onChange={e => setName(e.target.value)} />
</Form.Group>
<Form.Group controlId="formBasicDescription">
<Form.Label>Description</Form.Label>
<Form.Control as="textarea" rows={3} value={props.id != undefined ? description : ''} onChange={e => setDescription(e.target.value)} />
</Form.Group>
<Form.Group controlId="formBasicDate">
<Form.Label>Date</Form.Label>
<div>
<DatePicker
onChange={onChange}
value={props.id != undefined ? value : new Date()}
/>
</div>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={props.onHide}>
Close
</Button>
<Button variant="primary" onClick={saveEvent}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
)
};
export default React.memo(CreateOrUpdateEvent);
and I already remove StrictMode tag from index.js so please don't suggest that because it's not work for me.
The problem occurs, because every time you update a state, your axios function will be executed, updating the state again, causing an infinite loop.
Try to use the code below. Let me know if it made the trick.
useEffect(() => {
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}, [props.id])
It is posible that your error is related to the axios query to https://localhost:5001/Event/GetById/${props.id}.
Remember that setting a new value into your state makes a new render, so every time the query ends, it'll be called again.
You should put that query into a useEffect to just make it when the props.id changes:
function CreateOrUpdateEvent(props) {
const { token, setToken } = useToken();
const [value, onChange] = useState(new Date());
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const {userId} = TokenInfo();
useEffect(() => {
console.log("Add Event Rendered");
if(props.id != undefined) {
const headers = {
'Authorization': `bearer ${token}`
}
axios.get(`https://localhost:5001/Event/GetById/${props.id}`,
{
headers: headers
})
.then(function (res) {
setEventName(res.data.name);
setEventDescription(res.data.description);
setEventDate(res.data.eventDate);
})
.catch(function (error) {
console.log(error);
});
}
}, [props.id]);
...

React formik Create and update form in single form issues

I am using React formik I created Create form and update form in a single component. The update based on the One id if update means on page the id will be send in post method and get an data Database and the data show in the input field and update also working fine.But in the rendering component are slow and cause lot of errors and I want useEffect will be outside render function and the field working a slight delay after typing to display
Any Idea and any better Method
Thank you for Help
import React, { useEffect, useState } from "react";
import { Formik, Field, Form, FastField } from "formik";
import axios from "axios";
import {
Button,
TextField,
Grid,
makeStyles,
MenuItem,
FormControl,
Select,
InputLabel,
Checkbox,
FormControlLabel
} from "#material-ui/core/";
const baseurl = "http://localhost:8080/api/v1/";
const useStyles = makeStyles((theme) => ({
}));
function App() {
const classes = useStyles();
var PropertyId = sessionStorage.getItem("Propertyid");
const data = { propertyId: PropertyId };
const isAddMode = !PropertyId;
const initialValues = {
propertyName: "",
displayName: "",
description: "",
propertyType: ""
};
function onSubmit(fields, { setStatus }) {
setStatus();
if (isAddMode) {
createProperty(fields);
} else {
updateProperty(fields);
}
}
function createProperty(fields) {
axios
.post(baseurl + "propertybasicpropertydetails", fields, {
headers: {
"Access-Control-Allow-Origin": "*"
}
})
.then((res) => {
sessionStorage.setItem("Propertyid", JSON.stringify(res.data));
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
function updateProperty(fields) {
var updateData = { ...data, ...fields };
axios
.put(baseurl + "propertybasicpropertydetailsupdate", updateData, {
headers: {
"Access-Control-Allow-Origin": "*"
}
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div className={classes.paper}>
<Formik initialValues={initialValues} onSubmit={onSubmit}>
{({ setFieldValue }) => {
// eslint-disable-next-line
const [property, setProperty] = useState({});
// eslint-disable-next-line
// eslint-disable-next-line
useEffect(() => {
if (!isAddMode) {
const path = baseurl + "propertybasicpropertydetailsvalue";
axios
.post(path, data, {
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json"
}
})
.then((res) => {
console.log(res.data);
const fields = [
"propertyName",
];
fields.forEach((field) =>
setFieldValue(field, res.data[field], false)
);
setProperty(res.data);
});
}
}, [setFieldValue]);
return (
<Form>
<Grid container spacing={4}>
<Grid item xs={12} sm={6}>
<Field
name="propertyName"
type="text"
as={TextField}
fullWidth
label="Property Name"
autoFocus
variant="outlined"
size="small"
/>
</Grid>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.submit}
>
Save and Continue
</Button>
</Grid>
</Form>
);
}}
</Formik>
</div>
);
}
export { App };

Create a user document after creating a user in authentication in firebase

I'm trying to figure out how to create a user document in firestore after a user auth record is created.
My current attempt is below.
When i add the async/await the code generates error messages. When I remove them, the authentication part works to create a user record in the authentication part of firebase, but the firestore record is not created. No error message is generated.
Can anyone see what's going wrong?
import React, {useState} from 'react';
import { auth, firestore } from '../../../services/firebase/firebase';
import { useHistory } from 'react-router-dom';
import Button from '#material-ui/core/Button';
import TextField from '#material-ui/core/TextField';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import { Buttons } from '../navigation/styles';
export default function FormDialog() {
const [open, setOpen] = React.useState(false);
let [loading, setLoading] = useState(false);
const history = useHistory();
const [displayName, setDisplayName ] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleSubmit = async (event) => {
setLoading(true);
event.preventDefault();
auth.createUserWithEmailAndPassword( email, password)
THE ATTEMPT BELOW LOGS THE UID, BUT SAYS TypeError: user.updateProfile
is not a function
.then((res) => {
console.log("logging user", (auth.currentUser.uid) );
const user = auth.currentUser.uid;
return user.updateProfile({
displayName: displayName
})
firestore.collection('users').doc(auth.currentUser.uid)
.set({
fullName: displayName,
createdAt: firestore.fieldValue.serverTimestamp(),
})
})
THIS IS ANOTHER ATTEMPT, WHICH ASLO DOESNT WORK TO MAKE THE USER
DOCUMENT IN FIRESTORE
.then(() => {
if (auth.currentUser != null) {
auth.currentUser.updateProfile({
displayName: displayName
})
firestore.collection('users').doc(auth.currentUser.uid)
.set({
fullName: displayName,
createdAt: firestore.fieldValue.serverTimestamp(),
})
}
})
//THIS IS ANOTHER ATTEMPT, IN THE ALTERNATIVE TO THE ABOVE, WHICH ALSO
DOESNT WORK
.then((res) => {
const user = auth.currentUser;
return user.updateProfile({
displayName: displayName
})
firestore.collection('users').doc(auth.currentUser.uid)
.set({
fullName: displayName,
createdAt: firestore.fieldValue.serverTimestamp(),
})
})
.then(() => {
history.push("/");
})
.catch(error => {
console.error(error);
})
.then(() => {
clear();
})
.then(() => {
handleClose()
})
.finally(() => {
setLoading(false);
});
};
THIS IS A FURTHER ATTEMPT, WHICH I CAN'T TEST BECAUSE SOMETHING ABOUT THE THEN STATEMENT THAT TRIES TO PUSH HISTORY IS CONSIDERED TO HAVE A PARSING ERROR. I CAN'T FIND ANY TUTORIALS ABOUT HOW TO FIGURE OUT SOLVING THOSE.
const createUserDocument = async (user, displayName) => {
if (!user) return;
const userRef = firestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { email } = user;
const { displayName } = displayName;
try {
await userRef.set({
displayName,
email,
createdAt: new Date(),
});
} catch (error) {
console.log('Error in creating user', error);
}
}
};
const handleSubmit = async (event) => {
setLoading(true);
event.preventDefault();
try {
const { user } = await auth.createUserWithEmailAndPassword(
email,
password
);
await createUserDocument(user, { displayName });
} catch (error) {
console.log('error', error);
}
.then(() => {
history.push("/");
})
.then(() => {
clear();
})
.then(() => {
handleClose()
})
.finally(() => {
setLoading(false);
});
};
//continuing after all current attempts
const onChangeHandler = event => {
const { name, value } = event.currentTarget;
if (name === "userEmail") {
setEmail(value);
} else if (name === "userPassword") {
setPassword(value);
} else if (name === "displayName") {
setDisplayName(value);
}
};
const clear = () => {
setDisplayName("");
setEmail("");
setPassword("");
};
return (
<div>
<Buttons onClick={handleClickOpen}>
Join
</Buttons>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Join the waitlist</DialogTitle>
<DialogContent>
<DialogContentText>
Join
</DialogContentText>
<TextField
autoFocus
margin="dense"
label="Full name"
type="text"
fullWidth
name="displayName"
value={displayName}
placeholder="Jill Green"
id="displayName"
onChange={event => onChangeHandler(event)}
/>
<TextField
margin="dense"
label="Email Address"
type="email"
fullWidth
name="userEmail"
value={email}
placeholder="email address"
id="userEmail"
onChange={event => onChangeHandler(event)}
/>
<TextField
margin="dense"
label="Password"
type="password"
fullWidth
name="userPassword"
value={password}
id="userPassword"
placeholder="Minimum 6 characters"
onChange={event => onChangeHandler(event)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button
onClick={handleSubmit}
color="primary">
Register
</Button>
</DialogActions>
</Dialog>
</div>
);
}
Apart from ongoing issues trying to figure out how to record timestamps in firebase, this works to create the user document record.
const handleSubmit = async (event) => {
setLoading(true);
event.preventDefault();
auth.createUserWithEmailAndPassword(
email,
password
)
.then(credential => {
if (credential && credential.user) {
firestore.collection("users")
.doc(credential.user.uid)
.set({
email: email,
displayName: displayName,
// createdAt: firestore.Timestamp.now()
// createdAt: firestore.fieldValue.serverTimestamp()
// createdAt: firebase.firestore.fieldValue.serverTimestamp()
});
history.push("/");
}
})

Resources