API Calls with Axios fail on React - reactjs

On handleSubmit i am trying to log in using an API hosted on Heroku and made based on ExpressJS. It is working fine when i am using Postman to reach the API on the login endpoint. But when using axios on react it fails.
Heroku login endpoint : https://nodejs-mongodb-authapp.herokuapp.com/login
Code :
import React, { useState } from "react";
import './Login.css' ;
import {FaMountain} from 'react-icons/fa';
import { Form, Button } from "react-bootstrap";
import axios from "axios";
import Cookies from "universal-cookie";
const cookies = new Cookies();
export const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [login, setLogin] = useState(false);
const [state , setState] = useState(false);
const axios = require("axios");
const handleSubmit = (event) =>{
const data = JSON.stringify({
email: "au#outlook.com",
password: "zzz",
});
const config = {
method: "post",
url: "https://nodejs-mongodb-authapp.herokuapp.com/login",
headers: {
"Content-Type": "application/json",
},
data: data,
};
axios(config)
.then((result) => {
alert("in thenn");
// setLogin(true);
// cookies.set("TOKEN", result.data.token, {
// path: "/",
// });
// navigate('/auth');
})
.catch((error) => {
error = new Error();
});
}
return(
<div className="p1">
<div className="log">
<form className="formFields" >
<div className="lfa">
<FaMountain />
</div>
<p>LOG IN</p>
<Form onSubmit={(e)=>handleSubmit(e)} className="form_">
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e) => handleSubmit(e)}
>
Login
</Button>
</Form>
</form>
</div>
{/* {login ? (
<p className="text-success">You Are Logged in Successfully</p>
) : (
<p className="text-danger">You Are Not Logged in</p>
)} */}
</div>
)
}
The API is public for the moment you can try login with those credentials on PostMan :

Well, I managed to test it and it does respond. The request should be done like this:
import React, { useState } from "react";
import { FaMountain } from "react-icons/fa";
import { Form, Button } from "react-bootstrap";
import Cookies from "universal-cookie";
const cookies = new Cookies();
export const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [login, setLogin] = useState(false);
const axios = require("axios");
const handleSubmit = (event) => {
event.preventDefault();
const data = JSON.stringify({
email: "au#outlook.com",
password: "zzz"
});
const config = {
method: "post",
url: "https://nodejs-mongodb-authapp.herokuapp.com/login",
headers: {
"Content-Type": "application/json"
},
data: data
};
axios(config)
.then((result) => {
alert("in thenn");
console.log(result);
setLogin(true);
cookies.set("TOKEN", result.data.token, {
path: "/"
});
})
.catch((error) => {
error = new Error();
});
};
return (
<div className="p1">
<div className="log">
<div className="lfa">
<FaMountain />
</div>
<p>LOG IN (Login State {login.toString()})</p>
<p>Token: {cookies.get('TOKEN')}</p>
<Form onSubmit={(e) => handleSubmit(e)} className="form_">
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e) => handleSubmit(e)}
>
Login
</Button>
</Form>
</div>
{/* {login ? (
<p className="text-success">You Are Logged in Successfully</p>
) : (
<p className="text-danger">You Are Not Logged in</p>
)} */}
</div>
);
};
Since you are sending a JSON with the access information through the body, you have to take the JSON structure and then chain it to send it through post, and you have to add the ContentType: application/json header.
UP: When forms are used and you use an input type submit, every time the login button is clicked, the component is updated, to avoid this add the following inside the function event.preventDefault();
Test it here

Related

xhr.js:210 POST http://localhost:3000/api/users/login 401 (Unauthorized)

Hello I am developing a login system and when I try to make a post request to localhost:5000/api/users login I get:
xhr.js:210 POST http://localhost:5000/api/users/login 401 (Unauthorized)
this is my code:
import React, { useState, useEffect} from "react";
import { Form, Button, Row, Col } from "react-bootstrap";
import { Link } from "react-router-dom";
import "./LoginScreen.css";
import axios from 'axios';
function LoginScreen() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const submitHandler = async (e) => {
e.preventDefault();
try {
const config = {
headers: {
"Content-type": "application/json",
},
};
setLoading(true);
const { data } = await axios.post(
"http://localhost:5000/api/users/login",
{
email,
password,
},
config);
console.log(data);
localStorage.setItem("userInfo", JSON.stringify(data));
setLoading(false);
} catch (error) {
setError(error.response.data.message);
console.log(error);
}
};
return (
<div className="login_outer">
<h1>Login Here</h1>
<div className="loginContainer">
<Form onSubmit={ submitHandler }>
<Form.Group controlId="formBasicEmail" >
<Form.Label>E-mail: </Form.Label>
<Form.Control size="lg" type="email" value={email} placeholder="Enter Email" className="input" onChange={(e) => setEmail(e.target.value)}/>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password: </Form.Label>
<Form.Control size="lg" type="password" value={password} placeholder="Enter Password" className="input" onChange={(e) => setPassword(e.target.value)}/>
</Form.Group>
<Button className="login_button" variant="primary" type="submit">
Submit
</Button>
</Form>
<Row className="py-3">
<Col>
New User ? <Link to="/register">Register Here</Link>
</Col>
</Row>
</div>
</div>
);
}
export default LoginScreen;
Can anyone offer help with that ? I also have a proxy in the src folder. If I use postman for that request it does work.

Conditional statement to show registration success message is showing the error message even when successful?

I wasn't sure how to phrase the title but basically, I am following a tutorial to create a login/registration and I am currently trying to display a message indicating whether the registration attempt was successful or not.
Here is my Register.js
import React, { useState } from 'react';
import { Form, Button } from 'react-bootstrap';
import axios from "axios";
export default function Register() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [register, setRegister] = useState(false);
const handleSubmit = (e) => {
// prevent the form from refreshing the whole page
e.preventDefault();
//set configuration
const configuration = {
method: "post",
url: "https://nodejs-mongodb-auth-app-learn.herokuapp.com/register",
data: {
email,
password,
},
};
// API call
axios(configuration)
.then((result) => {
console.log(result);
setRegister=(true);
})
.catch((error) => {
error= new Error;
})
};
return (
<>
<h2>Register</h2>
<Form onSubmit={(e)=>handleSubmit(e)}>
{/* email */}
<Form.Group controlId="formBasicEmail">
<Form.Label>Email Address</Form.Label>
<Form.Control
type="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
</Form.Group>
{/* password */}
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter password"
/>
</Form.Group>
{/* submit button */}
<Button
variant="primary"
type="submit"
onClick={(e)=>handleSubmit(e)}
>
Register
</Button>
{/* display success message */}
{register ? (
<p className="text-success">You Are Registered Successfully</p>
) : (
<p className="text-danger">You Are Not Registered</p>
)}
</Form>
</>
)
};
The successful registration will log on the console, but either setRegister is not updating register to true, or my conditional statement is incorrect in some way?
It always shows "You Are Not Registered".
The correct way to ser an state using useState hook is:
e.g
const [register, setRegister] = useState(false);
setRegister(true)

Can't verify email and password from Firebase

I'm creating a signup form and trying to get the email and password to work. When I used input and set the state with the appropriate values, it works just fine, but once I wrap the Input around my custom component its unable to get data from the component into the state and gives me an error that a user cannot be found (even if their info is in the Firebase Auth)
I need help.
Auth.js
import style from "./auth.module.css";
import { useEffect, useRef, useState } from "react";
import { useAuthState } from 'react-firebase-hooks/auth';
import { auth, signInWithEmailAndPassword, signInWithGoogle } from "../../firebase";
import { CSSTransition } from "react-transition-group";
export default function Auth() {
const [activeMenu, setActiveMenu] = useState("main");
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [user, loading, error] = useAuthState(auth);
let domNode = useClickOutside(() => {
setActiveMenu(false);
});
return (
<div className={style.container}>
<Login />
<Signup />
</div>
);
function AuthType(props) {
return (
<a
href={props.link}
className={style.menuItem}
onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}
>
{props.children}
</a>
);
}
/* I believe you've switched up the login and sign-up? */
function Login() {
return (
<CSSTransition in={activeMenu === "main"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.login}>
<h1 className={style.title}>Clip It</h1>
{/* Email and Password */}
<Emailform
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Passform
label="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<div className={style.button}>
<input
type="submit"
value="Login"
onClick={() => signInWithEmailAndPassword(email, password)} />
<input
type="submit"
value="Login with Google"
onClick={signInWithGoogle} />
</div>
<div className={style.text}>
<p className={style.plink}>Forgot Password</p>
<div>
Need an account?
<AuthType goToMenu="Signup">click here</AuthType>
</div>
</div>
</div>
</div>
</CSSTransition>
);
}
function Signup() {
return (
<CSSTransition in={activeMenu === "Signup"} unmountOnExit timeout={500}>
<div ref={domNode}>
<div className={style.signUp}>
<div className={style.title}> Clip It</div>
<Form label="First Name" type="text" />
<Form label="Last Name" type="Text" />
<Form label="Email" type="email" />
<Form label="Date of Birth" type="date" />
<Form label="Password" type="password" />
<Form label="Confirm Password" type="password" />
<div className={style.button}>
<input type="submit" value="Sign Up" />
</div>
<div className={style.text}>
have an
<AuthType goToMenu="main"> account</AuthType>
</div>
</div>
</div>
</CSSTransition>
);
}
}
let useClickOutside = (handler) => {
let domNode = useRef();
useEffect(() => {
let clickListener = (event) => {
if (domNode.current && !domNode.current.contains(event.target)) {
handler();
}
};
document.addEventListener("mousedown", clickListener);
return () => {
document.removeEventListener("mousedown", clickListener);
};
});
return domNode;
};
function Form(props) {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type={props.input}
name={props.input}
required="true" />
</form>
</div>
);
}
function Emailform(props) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type="email"
name={props.input}
required="true"
value={email}
onChange={(e) => setEmail(e.target.value)} />
</form>
</div>
);
}
function Passform(props) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type="text"
name={props.input}
required="true"
value={password}
onChange={(e) => setPassword(e.target.value)} />
</form>
</div>
);
}
Firebase.js
import firebase from 'firebase/app';
//import * as firebase from "firebase/app";
import "firebase/auth"
import "firebase/firestore"
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyCq8BAlTWJXG7rFU95QkUTU8U0kXruPA9o",
authDomain: "clip-it-70ff5.firebaseapp.com",
databaseURL: "https://clip-it-70ff5-default-rtdb.firebaseio.com",
projectId: "clip-it-70ff5",
storageBucket: "clip-it-70ff5.appspot.com",
messagingSenderId: "637963668511",
appId: "1:637963668511:web:9cbd1deae03b819153d92a",
measurementId: "G-8S1G78ZH49"
};
const app = !firebase.apps.length ? firebase.initializeApp(firebaseConfig) : firebase.app();
const auth = app.auth();
const db = app.firestore();
/* Using Google Authentication */
const googleProvider = new firebase.auth.GoogleAuthProvider();
//
const signInWithGoogle = async () => {
try {
const res = await auth.signInWithPopup(googleProvider);
const user = res.user;
const query = await db
.collection("users")
.where("uid", "==", user.uid)
.get();
if (query.docs.length === 0) {
await db.collection("users").add({
uid: user.uid,
name: user.displayName,
authProvider: "google",
email: user.email,
});
alert("You're logged in");
}
} catch (err) {
console.error(err);
alert(err.message);
}
};
/* Using Email and Password */
// Sign/Logging In
const signInWithEmailAndPassword = async (email, password) => {
try {
await auth.signInWithEmailAndPassword(email.trim(), password);
alert("You've logged in successfuly");
} catch (err) {
console.error(err);
alert("The email or password is incorrect, please try again");
}
};
//SigningUp
const registerWithEmailAndPassword = async (name, email, password) => {
try {
const res = await auth.createUserWithEmailAndPassword(email.trim(), password);
const user = res.user;
await db.collection("users").add({
uid: user.uid,
name,
authProvider: "local",
email,
});
} catch (err) {
console.error(err);
alert(err.message);
}
};
//Sending Password reset link
const sendPasswordResetEmail = async (email) => {
try {
await auth.sendPasswordResetEmail(email);
alert("Password reset link sent!");
} catch (err) {
console.error(err);
alert(err.message);
}
};
const logout = () => {
auth.signOut();
}; // Log out
export {
signInWithGoogle,
signInWithEmailAndPassword,
registerWithEmailAndPassword,
sendPasswordResetEmail,
logout,
auth,
db,
};
Your code has a list of issues.
You have email/password states in both EmailForm/PassForm, as well as the parent (Auth) component.
You're setting values and trying to handle onChange on the EmailForm/PassForm components, but you never actually call these props, and you never actually set some of the props you're trying to access (ex: name={props.input}).
The inputs inside those two components are setting the email/password states to themselves, yet your Auth component is feeding Firebase its own states for email/password, which you never actually set.
You should also never use an input of type="text" for password fields.
If you insist on keeping the structure you currently have, move the EmailForm and PassForm functions inside to your Auth component, remove the props you're setting onto them, remove their states, and just use the Auth component's state.
// Inside the Auth component
const EmailForm = () => {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type="email"
name="email"
required="true"
value={email}
onChange={(e) => setEmail(e.target.value)} />
</form>
</div>
);
}
const PassForm = () => {
return (
<div className={style.formBox}>
<label className={style.label}>{props.label}:</label>
<form className={style.input}>
<input
type="password"
name="password"
required="true"
value={password}
onChange={(e) => setPassword(e.target.value)} />
</form>
</div>
);
}
Then call them with a simple
<EmailForm />
<PassForm />

Two times click is necessary to Login in ReactJS

I am trying to make a Login page and I am successful in some way. So here is my Login component:
import React, { useState, useEffect } from "react";
import Axios from "axios";
import useForm from "../components/LoginForm/useForm";
import validate from "components/LoginForm/validate";
import redtruck from "../assets/img/red-truck.png";
import auth from "../Authentication/auth";
import { withRouter } from "react-router";
const Login = ({ submitForm, history }) => {
const [isSubmitted, setIsSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const [login, setLogin] = useState(false);
async function submitForm() {
setIsSubmitted(true);
try {
await fetchLogin(values.email, values.password);
if(login){
auth.login(() => {
history.push("/admin");
});
}
} catch (e) {
auth.login(() => {
history.push("/");
})
}
}
const { handleChange, values, handleSubmit, errors } = useForm(
submitForm,
validate
);
useEffect(() => {
if (localStorage.getItem("user-info")) {
submitForm();
}
}, []);
const fetchLogin = async (email, password) => {
try {
setLoading(true);
const res = await Axios({
method: "POST",
url: `url`,
headers: {
},
data: {
user_email: email,
user_password: password,
},
});
if (res.status === 200) {
setLogin(true);
localStorage.setItem("user-info", JSON.stringify(res.data));
}
setLoading(false);
} catch (err) {
setError(err.message);
setLoading(false);
}
};
return (
<>
<div>
<div className="form-container">
<div className="form-content-left">
<img className="form-img" src={redtruck} alt="spaceship" />
</div>
<div className="form-content-right">
<h1>SIGN IN</h1>
<form className="form" onSubmit={handleSubmit}>
<div className="form-inputs">
<label htmlFor="email" className="form-label">
Email address
</label>
<input
id="signin-email"
type="email"
name="email"
placeholder="Enter email"
className="form-input"
value={values.email}
onChange={handleChange}
/>
{errors.email && <p>{errors.email}</p>}
</div>
<div className="form-inputs">
<label htmlFor="password" className="form-label">
Password
</label>
<input
id="signin-password"
type="password"
name="password"
placeholder="Password"
className="form-input"
value={values.password}
onChange={handleChange}
/>
{errors.password && <p>{errors.password}</p>}
{login ? "" : <p>The password or the email is wrong</p>}
</div>
<button
variant="primary"
type="submit"
className="form-input-btn"
>
LOGIN
</button>
</form>
</div>
</div>
</div>
</>
);
};
export default withRouter(Login);
So the login state is set to true when email and password are right for the user. Later I want to use it when redirecting page to "/admin". But my problem is I have to click twice to login in the first place. Besides I am not sure, if the catch part is right:
catch (e) {
auth.login(() => {
history.push("/");
})
}
So I would be really glad, if you can give me some hint about it.
Thanks...
it is not that you have to press twice, you can check component state, sometimes React batches setState and then update value. You can look at this setState doesn't update the state immediately

Testing a component that uses context

I'm trying to write tests for the Login component of my app, and want to test that the user can enter email and password fields, then submit them. There are two complications: it has a to another component, and it uses context to import a function called loginUser. The first problem I solve by making a custom renderWithRouter function, but I can't solve the second problem because the component isn't able to consume the loginUser function outside of context. I get this error: Error: Uncaught [TypeError: Cannot read property 'then' of undefined] because loginUser is undefined. Any idea how I can solve this? Here is Login.tsx:
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const [errors, setErrors] = useState<boolean>()
const history = useHistory()
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
And Login.test.tsx:
test("allows user to input their email", () => {
const onSubmit = jest.fn();
renderWithRouter(<Login />)
const input = screen.getByLabelText("Email address")
const pwd = screen.getByLabelText("Password")
const button = screen.getByText("Submit")
fireEvent.change(input, { target: { value: "t#t.com"}})
fireEvent.change(pwd, { target: { value: "123456"}})
fireEvent.click(button)
expect(onSubmit).toBeCalled()
})
})
Haven't run it but refactor to something like this and then test the LoginForm component.
import React, { useState } from 'react';
import { useAuth } from 'context/authContext'
import { useHistory, Link } from 'react-router-dom'
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button'
interface LoginFormProps {
onSubmit: (email: string, password: string) => void;
errors: boolean;
}
export const LoginForm: React.FC<LoginFormProps> = ({onSubmit, errors}) => {
const [email, setEmail] = useState<string>();
const [password, setPassword] = useState<string>();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
onSubmit(email, password)
}
return(
<div className="login-wrapper">
<h1>Please Log In</h1>
<div className='form-wrapper'>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
isInvalid={errors ? true : false}
type="email"
placeholder="Enter email"
onChange={e => setEmail(e.target.value)}/>
<Form.Control.Feedback type="invalid">
{errors ? 'either you don\'t have an account or the email and password do not match' : null}
</Form.Control.Feedback>
<Form.Text className="text-muted">
umm..
</Form.Text>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
onChange={e => setPassword(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
<div>
<Link to='/register'>new user? click here to register</Link>
</div>
</Form>
</div>
</div>
)
}
export const Login = (props: any) => {
const { setUser, loginUser } = useAuth()
const [errors, setErrors] = useState<boolean>(false)
const history = useHistory()
const handleSubmit = (email, password) => {
loginUser({
email,
password
}).then((result: any) => {
console.log(result)
if (result.message === 'login error') {
setErrors(true)
} else {
history.push('/dashboard/home')
}
})
}
return(
<LoginForm onSubmit={handleSubmit} errors={errors} />
)
}

Resources