react hooks: remember me checkbox in Login form not working - reactjs

I have a login form functional component built using react-hooks comprising of userid, password and rememberMe checkbox.
For some reason, the checkbox is not working as in, when I click on it, it does tick or untick. Which is basically its state is not changing.
userid, password and rememberMe checkbox needs to be sent to the backend. That's why I have to couple it with inputs. If it was an independent checkbox than it would have been easy.
I have handleChange() for userid, password and I have handleChechbox() for handling checkbox.
Below is the complete code.
const Login = (props) => {
const [inputs, setInputs] = useState({
userid: "",
password: "",
rememberPassword: false,
});
const [submitted, setSubmitted] = useState(false);
const { userid, password, rememberPassword } = inputs;
// reset login status
useEffect(() => {
dispatch(loginActions.logout());
}, []);
function handleChange(e) {
const { name, value } = e.target;
setInputs((inputs) => ({ ...inputs, [name]: value }));
}
function handleChechbox(e) {
const { name, value } = e.target;
console.log("eeeeee check", e.target.type);
console.log("eeeeee check", e.target.checked);
console.log("eeeeee check inputs", inputs);
console.log("eeeeee check inputs remember", inputs.rememberPassword);
if (e.target.type === "checkbox" && !e.target.checked) {
setInputs((inputs) => ({ ...inputs, [name]: "" }));
} else {
setInputs((inputs) => ({ ...inputs, [name]: value }));
}
}
function handleSubmit(e) {
e.preventDefault();
setSubmitted(true);
if (inputs) {
// get return url from location state or default to home page
const { from } = location.state || {
from: { pathname: "/admin/summary" },
};
dispatch(loginActions.login(inputs, from));
// props.history.push("/admin/summary");
}
}
return (
<div className="Login">
<div className="login-form-container">
<div className="content">
<Form className="login-form" onSubmit={handleSubmit}>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<input
autoFocus
type="email"
aria-label="Username"
aria-describedby="Username"
aria-invalid="false"
placeholder="Username or Email"
name="userid"
value={userid}
onChange={(event) => handleChange(event)}
className={
"form-control" + (submitted && !userid ? " is-invalid" : "")
}
/>
{submitted && !userid && (
<div className="invalid-feedback">
Username or Email is required
</div>
)}
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<input
type="password"
name="password"
placeholder="Password"
aria-label="password"
aria-describedby="password"
value={password}
onChange={(event) => handleChange(event)}
className={
"form-control" + (submitted && !password ? " is-invalid" : "")
}
/>
{submitted && !password && (
<div className="invalid-feedback">Password is required</div>
)}
</InputGroup>
<div className="form-actions">
<br />
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
id="rememberPassword"
name="checkbox"
checked={rememberPassword}
onChange={(event) => handleChechbox(event)}
// required
/>
<label className="form-check-label" for="rememberPassword">
Remember me
</label>
</div>
</div>
</Form>
</div>
</div>
</div>
);
};

You're reading the name attribute which is 'checkbox' not 'rememberPassword'. I'd imagine a simple console log of inputs would reveal you're setting the wrong property name.
Anyway your input is controlled, you don't need to read DOM values since you know that the value is and what it should change to.
function handleRememberMeChange(e) {
setInputs((inputs) => ({ ...inputs, rememberPassword: !inputs.rememberPassword }));
}
If you want it to work generically from an attribute then use id or change name to "rememberPassword". And use e.target.checked not e.target.value.
Also it's fine to do onChange={handleChechbox} instead of onChange={(event) => handleChechbox(event)} it's the same thing.

Related

How to add email validation to below code?

The below code is using cards to show account creation, deposit and other module.
I need to refractor the code to add email, name, and password field validation, but I am not able to do it.
context.js
const Route = ReactRouterDOM.Route;
const Link = ReactRouterDOM.Link;
const HashRouter = ReactRouterDOM.HashRouter;
const UserContext = React.createContext(null);
function Card(props){
function classes(){
const bg = props.bgcolor ? ' bg-' + props.bgcolor : ' ';
const txt = props.txtcolor ? ' text-' + props.txtcolor: ' text-white';
return 'card mb-3 ' + bg + txt;
}
return (
<div className={classes()} style={{maxWidth: "18rem"}}>
<div className="card-header">{props.header}</div>
<div className="card-body">
{props.title && (<h5 className="card-title">{props.title}</h5>)}
{props.text && (<p className="card-text">{props.text}</p>)}
{props.body}
{props.status && (<div id='createStatus'>{props.status}</div>)}
</div>
</div>
);
}
function CardForm(props) {
const ctx = React.useContext(UserContext);
return (
<>
<div style={{maxWidth: "18rem"}}>
<div className="name-field" style={{display: props.showName}}>
Name<br/>
<input type="input"
id="txtName"
className="form-control"
placeholder="Enter name"
onChange={e => ctx.name=e.currentTarget.value} /><br/>
</div>
<div className="email-field" style={{display: props.showEmail}}>
Email address<br/>
<input type="input"
id="txtEmail"
className="form-control"
placeholder="Enter email"
onChange={e => ctx.email=e.currentTarget.value}/><br/>
</div>
<div className="password-field" style={{display: props.showPassword}}>
Password<br/>
<input type="password"
id="txtPassword"
className="form-control"
placeholder="Enter password"
onChange={e => ctx.password=e.currentTarget.value}/><br/>
</div>
<div className="amount-field" style={{display: props.showAmount}}>
Amount<br/>
<input type="number"
className="form-control"
placeholder="Enter amount"
onChange={e => ctx.balance=e.currentTarget.value}/><br/>
</div>
</div>
</>
)
}
creatAccount.js
function CreateAccount(props){
const [show, setShow] = React.useState(true);
const [status, setStatus] = React.useState('');
const ctx = React.useContext(UserContext);
function addUser() {
ctx.balance = '0';
fetch(`/account/find/${ctx.email}`)
.then(response => response.json())
.then(data => {
console.log(data);
if (data.length===0) ctx.user = true;
})
.then(() => {
if (ctx.user===true) {
const url = `/account/create/${ctx.name}/${ctx.email}/${ctx.password}/${ctx.balance}`;
(async () => {
var res = await fetch(url);
var data = await res.json();
console.log(data);
})();
ctx.user='';
setShow(false);
} else {
ctx.user='';
setStatus('User already exists with that email');
setTimeout(() => setStatus(''),3000);
}})
}
return (
<Card
bgcolor="primary"
header="Create Account"
text=""
status={status}
body={
<>
{show ?
<>
<CardForm setShow={setShow} showAmount="none"/>
{<button type="submit" className="btn btn-light" onClick={addUser}>Create Account</button>}
</>
:
<Success setShow={setShow}/>}
</>
}
/>
);
}
function Success(props) {
return (
<>
<h5>Success!</h5><br/>
<button type="submit"
className="btn btn-light"
onClick={() => props.setShow(true)}>Add another account</button>
</>
)
}
I have tried multiple solutions from online content, but they do not solve the problem.
Lets simplify this
Using NPM package email-validator
Install via NPM
npm install email-validator
import * as EmailValidator from 'email-validator';
Bind your local state to your input field e.g
const [email, setEmail] = useState("");
const [valid, setIsValid] = useState(false);
<input
type="text"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
Create a function to check for email validation like below
const validateEmail = (email) => {
setIsValid(EmailValidator.validate(email));
};
Now there comes two cases. One is to show the validation on change the input field or check on submitting the whole form. To give the validation message while inputting email, you have to use the useEffect hook just like below
useEffect(() => {
if (email) {
validateEmail(email);
}
}, [email, validateEmail]);
You can use the valid state for validation message while user is entering the email something like below
<span>{valid ? 'Email format valid' : 'Invalid email format'} <span>
Second Case
just call the function we created above for email validation and check the valid state.

Why is my input field not editable even after adding onChange function - react

I am a new learner in react, I tried a simple login form which takes user input and passes it to backend. This application is also written to create tokens if the login credentials are correct. In order to take user input I have added the onChange() function and called it for each input field, however the it is still not editable. I couldn't find the error. I also have added onSubmit() function implementation. I did try various other ways of calling the onChange function, however it wasnt successful.
const [formData, setFormData] = useState({
clientEmail: "",
clientPassword: "",
errorMsg: false,
loadingSpinner: false,
});
// destructure form data
const { clientEmail, clientPassword, errorMsg, loadingSpinner } = formData;
const handleChange = (evt) => {
setFormData({
...formData,
[evt.target.name]: evt.target.value,
errorMsg: "",
});
};
const handleSubmit = (evt) => {
evt.preventDefault();
//form validation
if (isEmpty(clientEmail) || isEmpty(clientPassword)) {
setFormData({
...formData,
errorMsg: "All field are Required",
});
} else if (!isEmail(clientEmail)) {
setFormData({
...formData,
errorMsg: "Invalid Email new",
});
} else {
const { clientEmail, clientPassword} = formData;
const data = {
clientEmail,
clientPassword,
};
setFormData({
...formData,
loadingSpinner: true,
});
ClientLoginUser(data)
.then((response) => {
console.log(response);
setClientAuthentication(response.data.token, response.data.clients);
if (isClientAuthenticated()) {
console.log("client Successful");
history.push("./clientDashboard");
}
})
.catch((err) => {
console.log("client login api controller error: ", err);
setFormData({
...formData,
errorMsg:err.response.data.errorMessage,
loading:false
});
});
}
};
const showLoginForm = () => (
<Fragment>
<div className="card px-5 py-5">
<div className="card-body">
<h5 className="card-title text-center pb-3">Client Login</h5>
<form className="login-form"
onSubmit={handleSubmit}
noValidate>
{/* Email */}
<div className="form-group">
<input
type="email"
className="form-control"
name="email"
value={clientEmail}
placeholder="Email"
onChange={handleChange}
/>
</div>
{/* Password */}
<div className="form-group">
<input
type="password"
className="form-control"
name="password"
value={clientPassword}
placeholder="Password"
onChange={handleChange}
/>
</div>
{/* Submit button */}
<div className="form-group pt-3">
<button
type="submit"
className="btn btn-primary btn-lg btn-block"
>
Login
</button>
</div>
</form>
</div>
</div>
</Fragment>
);
/****************************
* Render
****************************/
return (
<div className="login-container">
<GridWrapper>
<div className="container-fluid">
<div className="row px-4 vh-100">
<div className="col-12 col-md-8 my-auto pl-5">
<img
src="/images/welcomeLogo.png"
className="img-fluid"
alt="Logo"
/>
</div>
<div className="col-12 col-md-3 align-self-center">
{errorMsg && showErrorMsg(errorMsg)}
{loadingSpinner && (
<div className="text-center pb-5">{showLoading()}</div>
)}
{showLoginForm()}
</div>
</div>
</div>
</GridWrapper>
</div>
);
};
// return <p>ClientLogin Component</p>;
The name in input is not exactly the same as in formData state
you got this state
const [formData, setFormData] = useState({
clientEmail: "",
clientPassword: "",
errorMsg: false,
loadingSpinner: false,
});
which contain data like clientEmail
<input
type="email"
className="form-control"
name="email"
value={clientEmail}
placeholder="Email"
onChange={handleChange}
/>
which contains name="email"
in handleChange Function
const handleChange = (evt) => {
setFormData({
...formData,
[evt.target.name]: evt.target.value,
errorMsg: "",
});
};
you contains [evt.target.name]: evt.target.value
thats mean you are trying to assign new Values to formData.email not to formData.clientEmail
there are two solution
first you can change
const [formData, setFormData] = useState({
email: "",
...
});
or you can
<input
type="email"
className="form-control"
name="clientEmail"
value={clientEmail}
placeholder="Email"
onChange={handleChange}
/>

react-hook-form do not send PasswordConfirm

Hello guys I'm using react-hook-form library
it's really awesome library for me !
but I have some problem at this time
I don't want to send pwdConfirm value, cuz this value gets me 400 erros,
so How can I send values without pwdConfirm ?
I just only want to send id , pwd , nickname !
const dispatch = useDispatch();
const { register, watch, errors, handleSubmit } = useForm();
const password = useRef();
password.current = watch('password');
const type = 'normal';
const onSubmit = (data) => {
console.log(data);
dispatch(signUpRequest(data));
};
useEffect(() => {});
return (
<>
<form
onSubmit={handleSubmit(onSubmit)}
className={styles.signup__form}
>
<label>email</label>
<input
name="email"
type="text"
ref={register({ required: true, pattern: /^\S+#\S+$/i })}
placeholder=""
/>
{errors.email && (
<p className={styles.error__message}>
incorrect email
</p>
)}
<label>password</label>
<input
name="password"
type="password"
ref={register({ required: true, minLength: 6 })}
placeholder=""
/>
{errors.password && errors.password.type === 'required' && (
<p className={styles.error__message}>
password plz
</p>
)}
{errors.password && errors.password.type === 'minLength' && (
<p className={styles.error__message}>
at least 6character
</p>
)}
<label>password confirm </label>
<input
type="password"
name="pwdConfirm"
ref={register({
required: true,
validate: (value) => value === password.current,
})}
placeholder=""
/>
{errors.pwdConfirm && errors.pwdConfirm.type === 'required' && (
<p className={styles.error__message}>
check your password
</p>
)}
{errors.pwdConfirm && errors.pwdConfirm.type === 'validate' && (
<p className={styles.error__message}>
not correct your password
</p>
)}
I have used the delete operator of javascript to remove pwdConfirm key from an existing object.
const onSubmit = (data) => {
delete data.pwdConfirm
console.log(data);
dispatch(signUpRequest(data));
};

React Functional Component: How to prevent form from submitting if validation failed

I need some help, I am new to react. I created a Functional Component form with Context API and I am trying to prevent it from submitting if the form failed validation and return false. I have check the validation and it is working by uncommenting the html input button and comment out the React Link (<Link></Link>) button. I try the code with class component and bind the handleChange and handleSubmit inside the constructor and it work: however, I do not want to used the class component. I just want to used the function component.
const Employee = () => {
const ctx = useContext(FormActualContext);
const handleChange = (event) => {
ctx.handleChange({
event,
type: 'employee',
});
};
const validate = () => {
const {
firstName,
lastName,
email,
dateOfBirth,
phoneNum,
} = ctx.formData.employee;
const { employee } = ctx.formErrors;
let {
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
} = employee;
firstNameError = !firstName ? 'First name can not be blank' : '';
lastNameError = lastName === '' ? 'Last name can not be blank' : '';
dateOfBirthError = !dateOfBirth ? 'Enter a valid date of birth' : '';
if (!validateEmail(email)) {
emailError =
email === '' ? 'Enter a valid email' : `${email} is not valid email`;
} else {
emailError = '';
}
if (!validatePhoneNumber(phoneNum)) {
phoneNumError =
phoneNum === ''
? 'Enter a valid phone'
: `${phoneNum} is not a valid phone number`;
} else {
phoneNumError = '';
}
if (
firstNameError ||
lastNameError ||
emailError ||
dateOfBirthError ||
phoneNumError
) {
ctx.setFormErrors({
employee: {
...employee,
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
},
});
return false;
}
return true;
};
const handleSubmit = (event) => {
event.preventDefault();
const isValid = validate();
if (isValid) {
ctx.reSetEmployee();
}
};
const {
employee: {
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
},
} = ctx.formErrors;
return (
<div className="container_fluid">
<div className="registration_form_container">
<div className="register_context">
<form action="" onSubmit={handleSubmit} className="registration_form">
<div className="form-group">
<input
type="text"
name="firstName"
id="firstName"
placeholder={'Enter first name'}
onChange={handleChange}
/>
<span>{firstNameError}</span>
</div>
<div className="form-group">
<input
type="text"
name="lastName"
id="lastName"
placeholder={'Enter last name'}
onChange={handleChange}
/>
<span>{lastNameError}</span>
</div>
<div className="form-group">
<input
type="text"
name="email"
id="email"
placeholder={'Enter email address'}
onChange={handleChange}
/>
<span>{emailError}</span>
</div>
<div className="form-group">
<input
type="date"
name="dateOfBirth"
id="dateOfBirth"
placeholder={'Enter date of birth'}
onChange={handleChange}
/>
<span>{dateOfBirthError}</span>
</div>
<div className="form-group">
<input
type="text"
name="phoneNum"
id="phoneNum"
placeholder={'Enter phone number (international: +1)'}
onChange={handleChange}
/>
<span>{phoneNumError}</span>
</div>
<div className="form-group custom_btn_container">
{/*<input type="submit" className="btn" value="Register"/>*/}
<Link to="/addressForm">
<input type="submit" className="btn" value="Register" />
</Link>
</div>
</form>
</div>
</div>
</div>
);
};
Issue
The issue isn't that the form is being submitted upon validation (true or false), but rather that both field validation and route navigation are more or less occurring at the same time. The Link, upon being clicked, will immediately navigate to the specified path.
Solution
Seems you want to validate the input, and only upon successful validation, call the reSetEmployee function on the context and navigate to "/addressForm".
I suggest rendering the submit button not within a Link and use imperative navigation. For this I'm assuming you are using react-router-dom library.
import { useHistory } from 'react-router-dom';
...
const history = useHistory();
...
const handleSubmit = (event) => {
event.preventDefault();
const isValid = validate();
if (isValid) {
ctx.reSetEmployee();
history.push("/addressForm");
}
};
...
<div className="form-group custom_btn_container">
<input type="submit" className="btn" value="Register"/>
</div>

How to pass a prop through history.push() in react?

I have a login component that has an email and password to log in. After a successful, I use this.props.history.push('/userComponent) to route a another component. At the component where it is routed should display the name of the person/user. I am trying to send as a prop but I end up getting undefined as the user component (the component to which it is routed) is not rendered. Please check the below.
export default class Login extends Component {
constructor(props) {
super(props);
this.x = '';
}
onSubmit = () => {
fetch('http://localhost:5000/api/account/')
.then(res => res.json())
.then(data => {
for (let index = 0; index < data.length; index++) {
if (userName === data[index].email && passWord === data[index].password && data[index].role === "Admin") {
console.log("login is successful");
this.x = true;
console.log('.......state after update: ' + this.x);
this.props.history.push('/userA');
return (
<div>
<UserA somePropName={data[index].userName} />
</div>
);
}
else if (userName === data[index].email && passWord === data[index].password) {
console.log("login is successful");
this.x = false;
console.log("x....... " + this.x);
this.props.history.push('/userB');
return (
<UserB somePropName={data[index].userName} />
);
}
else {
this.props.history.push('/errorPage');
}
}
});
}
render() {
return (
<div>
<div class="container">
<label for="uname"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required />
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required />
<button type="submit" onClick={this.onSubmit}>Login </button>
<label>
<input type="checkbox" checked="checked" name="remember" /> Remember me
</label>
</div>
<div class="container" style="background-color:#f1f1f1">
<button type="button" class="cancelbtn">Cancel</button>
<span class="psw">Forgot password?</span>
</div>
</div>
);
}
}
The way you can pass properties to other component while navigation
this.props.history.push({
pathname: '/userB',
state: { title: 'Hello world' }
})
using Link
<Link to={{
pathname: '/userB',
state: { title: 'Hello...' }
}}>Click</Link>
Access like this in navigated component
this.props.location.state.title
You should render conditionally UserA or UserB in render(). Place the user related fields in the state. Something like
render() {
return (
<div>
<div class="container">
<label for="uname"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required />
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required />
<button type="submit" onClick={this.onSubmit}>Login </button>
<label>
<input type="checkbox" checked="checked" name="remember" /> Remember me
</label>
</div>
<div class="container" style="background-color:#f1f1f1">
<button type="button" class="cancelbtn">Cancel</button>
<span class="psw">Forgot password?</span>
</div>
{this.state.isLoggedIn && this.state.isAdmin && <UserA somePropName={this.state.userName} />}
{this.state.isLoggedIn && !this.state.isAdmin && <UserB somePropName={this.state.userName} />}
</div>);
}
For the onSubmit func
onSubmit = () => {
fetch('http://localhost:5000/api/account/')
.then(res => res.json())
.then(data => {
this.props.history.push('/xxx');
this.setState({
isAdmin: data[index].role === "Admin"
isLoggedIn: userName === data[index].email && passWord === data[index].password,
userName: data[index].userName
})
})
}

Resources