Login Form use ReactJS (without database) with map()? - javascript

I want to create a login form with array data:
const users = [
{
username: 'admin1',
password: '12345678'
},
{
username:'admin2',
password:'012345678'
}
];
and Login.js looks something like this:
import React from 'react';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: "",
password: ""
};
}
changeInputValue(e) {
this.setState({
[e.target.name]: e.target.value
});
}
validationForm() {
let returnData = {
error : false,
msg: ''
}
const {password} = this.state
//Check password
if(password.length < 8) {
returnData = {
error: true,
msg: 'Password must be more than 8 characters'
}
}
return returnData;
}
submitForm(e) {
e.preventDefault();
const validation = this.validationForm()
var username = e.target.elements.username.value;
var password = e.target.elements.password.value;
if (validation.error) {
alert(validation.msg)
}else if(username === 'admin1' && password === '12345678') {
alert("Login successful");
}else {
alert("Wrong password or username");
}
}
render() {
return (
<div className="container" style={{ paddingTop: "5%" }}>
<form
onSubmit={e => {
this.submitForm(e);
}}
>
<div className="form-group">
<input
type="text"
className="form-control"
name="username"
placeholder="Username"
onChange={e => this.changeInputValue(e)}
/>
</div>
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
onChange={e => this.changeInputValue(e)}
/>
</div>
<button value="submit" className="btn btn-primary" onClick={this.postDetails}>
Submit
</button>
</form>
</div>
);
}
}
export default Login;
So the above code only check whether the username and password fields entered in the form match the name and password of single record in the array of objects of User.js the above code is working fine. I don't know how to check username and password from the passed array.
I want to use map () to check for username and password. Please show an instance of how it is done. Sorry, I'm new to ReactJS so I'm a bit confused, hope you can help. Thanks

You can use .some() method.
doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
Something like this
var username = e.target.elements.username.value;
var password = e.target.elements.password.value;
if(users.some((elem) => elem.username === username && elem.password === password)) {
// when authentication is valid
} else {
// not a valid username / password
}

You can use find
const user = users.find(user => (user.username === username && user.password === password))
if(user){
//correct user
}
if(!user) {
//incorrect
}

Related

ReactJS: Login form with array of users

I want to create a login form with array data in User.js file (without any backend)
My User.js file looks like this:
const users = [
{
username: 'admin1',
password: 'admin1#1'
},
{
username:'admin2',
password:'admin2#2'
}
];
and my Login.js looks something like this:
import React, { Component, Fragment } from 'react';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
userName: "",
password: ""
};
}
changeInputValue(e) {
this.setState({
[e.target.name]: e.target.value
});
}
validationForm() {
let returnData = {
error : false,
msg: ''
}
const {password} = this.state
//Check password
if(password.length < 8) {
returnData = {
error: true,
msg: 'Password must be more than 8 characters'
}
}
return returnData;
}
submitForm(e) {
e.preventDefault();
const validation = this.validationForm()
var username = e.target.elements.username.value;
var password = e.target.elements.password.value;
if (validation.error) {
alert(validation.msg)
}else if(username === 'admin2' && password === 'admin1#1') {
alert("Login successful");
}else {
alert("Wrong password or username");
}
}
render() {
return (
<div className="container" style={{ paddingTop: "5%" }}>
<form
onSubmit={e => {
this.submitForm(e);
}}
>
<div className="form-group">
<input
type="text"
className="form-control"
name="username"
placeholder="Username"
onChange={e => this.changeInputValue(e)}
/>
</div>
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
onChange={e => this.changeInputValue(e)}
/>
</div>
<button value="submit" className="btn btn-primary" onClick={this.postDetails}>
Submit
</button>
</form>
</div>
);
}
}
export default Login;
Above, I don't know how to check username and password from the passed array. Please show an instance of how it is done. And after successful login, how do I switch to another page?Sorry, I'm new to ReactJS so I'm a bit confused, hope you can help.
Import the data from user.js after u exported it from there.
export const UsersData = [
{
username: 'admin1',
password: 'admin1#1'
},
{
username:'admin2',
password:'admin2#2'
}
];
//Login.js
import UsersData from './user.js'
Now do array operation on this array of objects. Now you can convert the data entered by the user to a similar format provided by User.js.
ie: If the user entered username = alphabeta, password = 1234
submitForm(e) {
e.preventDefault();
const validation = this.validationForm()
var inputData = {
username : e.target.elements.username.value,
password : e.target.elements.password.value
};
if (validation.error) {
alert(validation.msg)
}else if(UsersData.findIndex(inputData)!==-1) {
alert("Login successful");
}else {
alert("Wrong password or username");
}
}
Here we uses an array operation called findIndex which returns -1 if it doesn't find the object in the array else it returns the index.
To know more about Array Opreration in JS : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
Not sure if I understand correstly your question but here is how you can check if the duo user and password are in your array :
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
const App = () => {
const users = [
{
username: "admin1",
password: "admin1#1",
},
{
username: "admin2",
password: "admin2#2",
},
];
const handleSubmit = (e) => {
e.preventDefault();
const toCompare = {
username: e.target.elements.username.value,
password: e.target.elements.password.value,
};
// Or just compare properties
if (users.some(u => JSON.stringify(u) === JSON.stringify(toCompare))) {
console.log('User exists in array');
} else {
console.log('User does not exist in array');
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="username" name="username" />
<input type="text" placeholder="password" name="password" />
<button type="submit">Send</button>
</form>
);
};
render(<App />, document.getElementById('root'));
And here is the repro on Stackblitz

In reactjs console.log is not working using onSubmit for form

I am new to react. Please advise why the console.log() is not showing on the console after submitting the form.
In handleSubmit function e.preventDefault() and this.setState({ errors }) is executing properly but not console log.
Please advise what I am doing wrong in below code.
react version: 16.14.0
Windows OS
import React, { Component } from "react";
import Input from "./common/input";
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
account: {
username: "",
password: "",
},
errors: {},
};
}
handleChange = ({ currentTarget: input }) => {
const account = { ...this.state.account };
account[input.name] = input.value;
this.setState({ account: account });
};
validate = () => {
const errors = {};
const { account } = this.state;
if (account.username.trim() === "")
errors.username = "Username is required";
if (account.password.trim() === "")
errors.password = "Password is required";
return Object.keys(errors).length === 0 ? null : errors;
};
handleSubmit = (e) => {
e.preventDefault();
const errors = this.validate();
console.log(errors); // If errors it is not showing anything on console
this.setState({ errors });
if (errors) return;
console.log("Submitted"); // if no errors this message should show on console but its not
};
render() {
const { account } = this.state;
return (
<div>
<h3>LoginForm</h3>
<form onSubmit={this.handleSubmit}>
<Input
name="username"
value={account.username}
onChange={this.handleChange}
Label="Username"
/>
<Input
name="password"
value={account.password}
onChange={this.handleChange}
Label="Password"
/>
<button type="submit" className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
Edit:
I used this exact same code in another application and its working.
Please advise also on any specific app settings need to look for?
code editor: Visual Studio
Browser: Chrome
Thanks!

Prop does not change from true to false in react, redux, node app

As the title says, I have a check in RegistrationForm for checking if user email exists in the database. The prop is userExists. Now, if it's true, that means User email exists, so the user should be shown a message that says- "User already exists." Now, if it's false, he should be successfully registered and redirected to login.
But right now, I cannot even register with any email. In the network tab, it returns a 200 and gives
the response:
{"message":"User with this email does not exist"}
Can someone show me the proper way to do it? I mean the check, and the message from the backend, through redux, and back to the component.
My code is jibberish.
RegistrationForm Component
import React, { Component } from "react";
import { registerUser, checkValidUser } from "../../actions/userActions";
import { connect } from "react-redux";
import validator from "validator";
import { Link } from "react-router-dom";
import { toastError } from "../../../utils/toastify";
class RegistrationForm extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
email: "",
password: "",
};
}
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
handleSubmit = async (event) => {
event.preventDefault();
const { username, email, password } = this.state;
const registrationData = {
username: this.state.username,
email: this.state.email,
password: this.state.password,
};
if (!username || !email || !password) {
return toastError("Credentials should not be empty");
}
if (username.length < 6) {
return toastError("Username should be greater than 6 characters.");
}
if (!validator.isEmail(email)) {
return toastError("Invalid email.");
}
if (password.length < 6) {
return toastError("Password must contain 6 characters.");
}
await this.props.dispatch(checkUserExists(email));
const userExists = this.props.userExists;
if (!userExists) {
this.props.dispatch(
registerUser(registrationData, () => {
this.props.history.push("/login");
})
);
} else {
toastError("User with this email already exisits"); // I'm not sure how to show the message if the user email already exists. I want to show the message from backend, but currently I'm just doing it manually
}
};
render() {
const isRegistrationInProgress = this.props.isRegistrationInProgress;
return (
<div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input
onChange={this.handleChange}
name="username"
value={this.state.username}
className="input"
type="text"
placeholder="Username"
/>
<span className="icon is-small is-left">
<i className="fas fa-user"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input
onChange={this.handleChange}
name="email"
value={this.state.email}
className="input"
type="email"
placeholder="Email"
/>
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left">
<input
onChange={this.handleChange}
name="password"
value={this.state.password}
className="input"
type="password"
placeholder="Password"
/>
<span className="icon is-small is-left">
<i className="fas fa-lock"></i>
</span>
</p>
</div>
<div className="field">
<div className="control">
{isRegistrationInProgress ? (
<button className="button is-success is-loading">Sign Up</button>
) : (
<button onClick={this.handleSubmit} className="button is-success">
Sign up console.log("registrationData", registrationData)
</button>
)}
<Link to="/login">
<p className="has-text-danger">
Already have an account? Sign In
</p>
</Link>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
isRegistrationInProgress: state.registration.isRegistrationInProgress,
userExists: state.registration.userExists,
};
};
export default connect(mapStateToProps)(RegistrationForm);
checkUserExists action
export const checkUserExists = (email) => {
return async (dispatch) => {
try {
const res = await axios.get(`${baseUrl}/users/checkUserExists/${email}`)
console.log("res=>", res)
if (res.data.message = "User with this email already exists") {
dispatch({
type: "CHECK_USER_EXISTS_SUCCESS",
})
}
} catch (err) {
console.log("error=>", err)
}
}
}
checkUserExists controller function
checkUserExists: async (req, res, next) => {
const { email } = req.params
try {
const user = await User.findOne({ email })
if (user) {
return res.status(200).json({ message: "User with this email already exists" })
} else {
return res.json({ message: "User with this email does not exist" })
}
} catch (error) {
return next(error)
}
}
registerUser action
export const registerUser = (registrationData, redirect) => {
return async (dispatch) => {
dispatch({ type: "REGISTRATION_STARTS" })
try {
const res = await axios.post(
`${baseUrl}/users/register`,
registrationData
)
dispatch({
type: "REGISTRATION_SUCCESS",
data: { user: res.data.user },
})
toastSuccess("Successfully registered")
redirect()
} catch (err) {
dispatch({
type: "REGISTRATION_ERROR",
data: { error: err },
})
}
}
}
registration reducer
const initialState = () => ({
isRegistrationInProgress: false,
isRegistered: false,
registrationError: null,
user: {},
userExists: false,
error: null,
});
const registration = (state = initialState, action) => {
switch (action.type) {
case "REGISTRATION_STARTS":
return {
...state,
isRegistrationInProgress: true,
registrationError: null,
};
case "REGISTRATION_SUCCESS":
return {
...state,
isRegistrationInProgress: false,
registrationError: null,
isRegistered: true,
user: action.data,
};
case "REGISTRATION_ERROR":
return {
...state,
isRegistrationInProgress: false,
registrationError: action.data.error,
isRegistered: false,
user: {},
};
case "CHECK_USER_EXISTS_SUCCESS":
return {
...state,
userExists: true,
error: null
};
default:
return state;
}
};
export default registration;

How do I check if email already exists using MERN

I have a registration view and I'm trying to check whether the email already exists, I've undone the react code so you can get a good idea of the structure.
The emails are set as unique in the schema.
AuthController
const { check, validationResult } = require("express-validator");
const jwt = require("jsonwebtoken");
const passport = require("passport");
require("../passport");
const Users = require("../models/user");
let validations = [
check("email")
.isEmail()
.withMessage("The email you have entered is not valid")
.contains("#")
.withMessage("The email you have entered does not contain an #"),
check("password")
.isLength({ min: 5 })
.withMessage("The password must have at least 5 characters")
];
// Throw error if the key doesn't exist
if (!process.env.JWT_SECRET) {
console.error("Cannot find JWT key");
}
function generateWebToken(user) {
return jwt.sign(user, process.env.JWT_SECRET, {
subject: user.email,
expiresIn: "7d",
algorithm: "HS256"
});
}
/* POST register a user if one doesn't already exist */
module.exports.register = [
...validations,
(req, res) => {
// Get validation errors from the request
const errors = validationResult(req);
// Return the errors
if (!errors.isEmpty()) {
return res.status(422).json({ error: errors.array() });
}
let hashedPassword = Users.hashPassword(req.body.password);
Users.findOne({ email: req.body.email })
.then(function(user) {
if (user) {
return res
.status(400)
.send(`An account with the email ${req.body.email} already exists`);
} else {
Users.create({
email: req.body.email,
password: hashedPassword
})
.then(function(user) {
res.status(201).json(user);
})
.catch(function(err) {
console.error(err);
res.status(500).send(`Error ${err}`);
});
}
})
.catch(function(err) {
console.error(err);
res.status(500).send(`Error ${err}`);
});
}
];
Register.js (react component)
import React, { Component } from "react";
const initalState = {
email: "",
password: "",
emailErr: "",
passwordErr: ""
};
class Register extends Component {
constructor(props) {
super(props);
this.state = {
initalState,
successMsg: ""
};
this.handleSubmit = this.handleSubmit.bind(this);
}
validate = () => {
let emailErr = "";
let passwordErr = "";
// Email validation
if (!this.state.email) {
emailErr = "Please enter an email";
}
// Password validation
if (!this.state.password) {
passwordErr = "Please enter your password";
}
if (emailErr || passwordErr) {
this.setState({ emailErr, passwordErr });
return false;
}
return true;
};
onEmailChange(event) {
this.setState({ email: event.target.value });
}
onPasswordChange(event) {
this.setState({ password: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const isValid = this.validate();
if (isValid) {
fetch("/api/auth/register", {
method: "POST",
body: JSON.stringify(this.state),
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(this.setState({ successMsg: true }), this.setState(initalState));
}
}
render() {
return (
<>
<div className='block md:flex md:flex-column h-full'>
<div className='p-12 w-full text-center text-gray-800'>
<h1 className='title'>Register</h1>
{this.state.successMsg && (
<div
className='w-full m-auto max-w-lg mb-10 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative'
role='alert'
>
<strong className='font-bold'>Holy smokes! </strong>
<span className='block sm:inline'>
Account has been created
</span>
</div>
)}
<form className='w-full m-auto max-w-lg'>
<div className='flex flex-wrap -mx-3'>
<div className='w-full px-3 mb-6'>
<label htmlFor='email'>Email Address</label>
<input
id='email'
type='text'
placeholder='johndoe#example.co.uk'
value={this.state.email || ""}
onChange={this.onEmailChange.bind(this)}
/>
<p className='my-2 text-red-500 text-xs'>
{this.state.emailErr}
</p>
</div>
</div>
<div className='flex flex-wrap -mx-3'>
<div className='w-full px-3 mb-6'>
<label htmlFor='password'>Password</label>
<input
id='password'
type='password'
placeholder='*********'
value={this.state.password || ""}
onChange={this.onPasswordChange.bind(this)}
/>
<p className='my-2 text-red-500 text-xs'>
{this.state.passwordErr}
</p>
</div>
</div>
<div className='flex'>
<button
className='btn'
type='button'
onClick={this.handleSubmit.bind(this)}
>
Send
</button>
</div>
</form>
</div>
</div>
</>
);
}
}
export default Register;
Not sure if it's possible just to pass the express validations or what the best solution for this is.
The code above looks fine as this line of code explicitly checks for email duplications in MongoDB
>if (user) {
> return res
> .status(400)
> .send(`An account with the email ${req.body.email} already exists`);
> } else {...
Here is a full solution to display errors from register api (both express-validations and user is already registered error)
I tried to mimic your api and react app, but heavily refactored:
1-) In api we need to convert express validation errors in a readable way:
Normally its validation errors are in an array like this:
{
"error": [
{
"value": "abcdef#net",
"msg": "The email you have entered is not valid",
"param": "email",
"location": "body"
},
{
"value": "12",
"msg": "The password must have at least 5 characters",
"param": "password",
"location": "body"
}
]
}
With the following code, I converted the error messages in this format to easily access in react app by field name:
{
"errors": {
"email": "The email you have entered is not valid",
"password": "The password must have at least 5 characters"
}
}
register route:
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const { User } = require("../models/user");
const express = require("express");
const router = express.Router();
let validations = [
check("email")
.isEmail()
.withMessage("The email you have entered is not valid"),
check("password")
.isLength({ min: 5 })
.withMessage("The password must have at least 5 characters")
];
router.post("/register", validations, async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
const err = {};
errors.array().forEach(error => {
err[error.param] = error.msg;
});
return res.status(422).json({ errors: err });
}
const { email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) return res.status(400).send("User already registered.");
user = new User({ email, password });
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
user = await user.save();
res.send(user);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
module.exports = router;
In the react side I used axios instead of fetch since the error handling is far easier with axios:
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
email: "",
password: "",
fieldErrors: {},
registerError: null
};
handleSubmit = async e => {
e.preventDefault();
this.setState({
fieldErrors: {},
registerError: null
})
const { email, password } = this.state;
try {
const response = await axios.post(
"https://express-validator-auth.herokuapp.com/api/users/register",
{ email, password }
);
console.log(response.data);
alert("Registered");
} catch (error) {
if (error.response && error.response.data) {
if (error.response.data.errors) {
this.setState({
fieldErrors: error.response.data.errors
});
} else {
this.setState({
registerError: error.response.data
});
}
} else {
console.log(error);
}
}
};
handleChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
const { email, password, fieldErrors, registerError } = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Email</label>
<input
type="text"
className={
!fieldErrors.email ? "form-control" : "form-control is-invalid"
}
name="email"
value={email}
onChange={this.handleChange}
/>
{fieldErrors.email && (
<div className="invalid-feedback">{fieldErrors.email}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
className={
!fieldErrors.password
? "form-control"
: "form-control is-invalid"
}
name="password"
value={password}
onChange={this.handleChange}
/>
{fieldErrors.password && (
<div className="invalid-feedback">{fieldErrors.password}</div>
)}
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
{registerError && (
<div className="alert alert-danger" role="alert">
{registerError}
</div>
)}
</form>
<br />
{JSON.stringify(this.state)}
</div>
);
}
}
export default App;
So when an validation error occurres form will look like this:
And when the "user is already registered error" occurres form validation will look like this:
The react app can be accessed from this codesandbox:
https://codesandbox.io/s/gracious-mccarthy-kluor

React onchange event: this.setState() not setting state?

I am trying to set state via this.setState() of username and password when user types a value in username and password field. I am using onChange event type
ISSUE: the state is not changing. I log this.state.data.username in the render().
import React, { Component } from "react";
import { Form, Button } from "react-bootstrap";
import { Link } from "react-router-dom";
var Joi = require("joi-browser");
class Login extends Component {
state = {
data: { username: "a", password: "b " },
errors: {
email: "ddsfds",
password: "aaaa"
}
};
schema = {
username: Joi.string()
.min(0)
.required()
.label("Username"),
password: Joi.string()
.required()
.label("Password")
};
handleSubmit = event => {
event.preventDefault();
console.log("submited.", event.target);
const { data } = this.state;
const { err } = Joi.validate(data, this.schema);
if (err) {
console.log("error is true", err);
} else {
console.log("not true");
}
};
handleEmailOnChange = event => {
const inputUsername = event.target.value;
console.log("input is...", inputUsername);
this.setState({ username: inputUsername });
};
handlePassword = event => {
const passwordInput = event.target.value;
this.setState({ password: passwordInput });
};
render() {
console.log("username ", this.state.data.username);
return (
<div id="form-wrapper">
<Form>
<Form.Group controlId="formBasicEmail">
<h4>Sign In</h4>
<Form.Control
type="email"
placeholder="Enter email"
onChange={this.handleEmailOnChange}
/>
{/* <span>{this.state.errors.username} </span> */}
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Control
type="password"
placeholder="Password"
onChange={this.handlePassword}
/>
</Form.Group>
<div id="register-wrapper">
<Link to="/register" type="button" className="btn btn-warning">
Register Account
</Link>
<Button
variant="primary"
className="m-2"
type="submit"
onClick={this.handleSubmit}
>
Submit
</Button>
</div>
</Form>
</div>
);
}
}
export default Login;
You aren't updating the state correctly or not using it correctly. The state in your constructor has data object with username and password
handleEmailOnChange = event => {
const inputUsername = event.target.value;
console.log("input is...", inputUsername);
this.setState(prev => ({data: {...prev.data, username: inputUsername } }));
};
handlePassword = event => {
const passwordInput = event.target.value;
this.setState(prev => ({data: {...prev.data, password: passwordInput } }));
};
The state you are changing is this.state.username, the one you console is this.state.data.username.
To set data in your state, use:
this.setState(prevState => ({
data: {
username: inputUsername,
...prevState.data
}
})

Categories