Change multiple elements simultaneously - reactjs

I have a button which upon clicking, should make the password field blank. I also intend to change the text of the button from Change Password to Save password at the same time. In short, I would like these changes to happen at the same time. I have taken a toggle variable using which I'm able to change the text of the button but do not know how to do the same for the password field.
Here is my code:
import { useState } from "react";
import { useHistory } from "react-router";
const User = () => {
const [username, setUsername] = useState()
const [password, setPassword] = useState()
const [toggle, setToggle] = useState(false);
const history = useHistory();
const login = () => {
// if (username === 'admin' & password === 'admin') {
// history.push('/home')
// } else {
// alert('wrong credentials')
// }
setToggle(!toggle);
}
return (
<>
<div className="container">
<label for="username">UserName:</label>
<input
className="form-control"
type='text'
id="username"
name="username"
value={username}
onChange={(evt) => setUsername(evt.target.value)}
/>
<br />
<label for="password">Password:</label>
<input
className="form-control"
type='text'
id="password"
name="password"
value={password}
onChange={(evt) => setPassword(evt.target.value)}
/>
<button className="btn btn-primary mt-5 " onClick={login}>{toggle ? 'SavePassword' : ' change password '}</button>
<button className="btn btn-primary mt-5 ml-5 pull-right" onClick={login}>Log out</button>
</div>
</>
)
}
export default User;

Fastest answer is:
const login = () => {
setPassword('');
setToggle(!toggle);
}
This solution has a problem: every time you run login function your component is re-rendered twice.
To avoid this, there are several ways; the first one could be join username, password and toggle into the same state. Something like:
const [state, setState] = useState({
username: null,
password: null,
toggle: false
});
...
const login = () => {
setState((prevState) => {
return {
toggle: !prevState.toggle,
username: prevState.username
password: "",
}
});
}

Related

Cannot register. Username and password did not pass in the payload when using useRef

I tried to pass the username and password input by using useRef() for the registration process through the register form. After click button to submit it, it said required username and password. I check the network payload at browser, it only contain email without username and password.
Below are the code
import { useRef, useState } from "react";
import "./register.scss";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const Register = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [username, setUsername] = useState("");
const navigate = useNavigate();
const emailRef = useRef();
const passwordRef = useRef();
const usernameRef = useRef();
// Send email to appear password
const handleStart = () => {
setEmail(emailRef.current.value);
};
// Send username & password for membership
const handleFinish = async (e) => {
e.preventDefault();
setPassword(passwordRef.current.value);
setUsername(usernameRef.current.value);
try {
await axios.post("auth/register", { username, email, password });
navigate("/login");
} catch (err) {
console.log(err);
}
};
return (
<div className="register">
<div className="wrapper">
<div className="header">
<img src="./assets/logo.png" alt="" className="logo" />
<button className="login-btn">Sign In</button>
</div>
</div>
<div className="container">
<h1>Unlimited movies, TV shows and more</h1>
<h2>Watch anywhere. Cancel anytime.</h2>
<p>
Ready to watch? Enter your email to create or restart your membership.
</p>
{!email ? (
<div className="input">
<input type="email" placeholder="Email address" ref={emailRef} />
<button className="register-btn" onClick={handleStart}>
Get Started
</button>
</div>
) : (
<form className="input">
<input type="username" placeholder="Username" ref={usernameRef} />
<input type="password" placeholder="Password" ref={passwordRef} />
<button className="register-btn" onClick={handleFinish}>
Start
</button>
</form>
)}
</div>
</div>
);
};
export default Register;
Here are the screenshot for network payload
Payload
[Preview2
You're trying to access state that hasn't update yet.
If you're using refs, you can remove the useState hooks and change your code to something like below.
const handleFinish = async (e) => {
e.preventDefault();
try {
await axios.post("auth/register", { username: usernameRef.current.value , email: emailRef.current.value, password: passwordRef.current.value });
navigate("/login");
} catch (err) {
console.log(err);
}
Controlled components would be a better option for handling form elements imo.
https://reactjs.org/docs/forms.html#controlled-components

How to activate button in condition

const Login = () => {
const [ID, setID] = useState();
const [PW, setPW] = useState();
function handleIdInput(event) {
setID(event.target.value);
console.log(ID);
}
function handlePWInput(event) {
setPW(event.target.value);
console.log(PW);
}
<form>
<div>
<input type="text" id="ID" className="boxes" onChange={handleIdInput} />
</div>
<div>
<input
type="password"
id="password"
className="boxes"
onChange={handlePWInput}
/>
</div>
<button className="boxes" type="button" onClick={goList}>
LogIn
</button>
</form>;
};
I want to activate the button if id includes # and password has at least 7 characters. I'm trying to find a way not to use class and this method. Thank you!
Please make use of disabled property.
<button className='boxes' type='button' onClick={goList} disabled={()=> checkDisabled(ID, PW)}>
LogIn
</button>
The checkDisabled function must return true or false based on your requirements.
const checkDisabled = (id, password) => {
// condition here
if(id.includes('#') && password.length >= 7)
{
return false;
}
return true;
}
Then finally the component code must look like this. Please note the changes I have made related to how the state variables are connected with the input component.
import { useState } from "react";
const Login = () => {
const [ID, setID] = useState("");
const [PW, setPW] = useState("");
function handleIdInput(event) {
setID(event.target.value);
console.log(ID);
}
function handlePWInput(event) {
setPW(event.target.value);
console.log(PW);
}
const checkDisabled = (id, password) => {
// condition here
if (id.includes("#") && password.length >= 7) {
return false;
}
return true;
};
return (
<form>
<div>
<input
type="text"
id="ID"
className="boxes"
value={ID}
onChange={(e) => handleIdInput(e)}
/>
</div>
<div>
<input
type="password"
id="password"
className="boxes"
value={PW}
onChange={(e) => handlePWInput(e)}
/>
</div>
<button
className="boxes"
type="button"
onClick={goList}
disabled={() => checkDisabled(ID, PW)}
>
LogIn
</button>
</form>
);
};
export default Login;
Reference for useState hook.

How to validate email and password using react hooks?

I am getting state values while clicking submit button but I am unable to do the validation for my login form and how to display the error messages below the input field when I enter my input wrong or empty. please give me a solution to this.Thanks in advance.
const Login = () => {
const [state, setState] = useState({
email: "",
password: ""
});
const handleChange = (e) => {
const {id, value} = e.target
setState(prevState => ({
...prevState,
[id]: value
}))
}
const handleSubmitClick = (e) => {
e.preventDefault();
console.log("Authenticated",state);
}
return(
<>
<div className="container">
<div className="title">
<form onSubmit={handleSubmitClick}>
<div className="form-group">
<input
type="email"
className="email"
placeholder="Email"
value={state.email}
onChange={handleChange}/>
</div>
<div className="form-group">
<input
type="password"
className="password"
placeholder="Password"
value={state.password}
onChange={handleChange}/>
</div>
<button type="submit" className="button">Enter</button>
</form>
</div>
</div>
</>
)
}
export default Login;
If you want to perform client-side validation, you can create hook like this:
const useEmailValidation = (email) => {
const isEmailValid = /#/.test(email); // use any validator you want
return isEmailValid;
};
And then you can use this hook in your form component:
...
const isEmailValid = useEmailValidation(state.email);
const isPasswordValid = usePasswordValidation(state.password);
const isFormValid = isEmailValid && isPasswordValid;
return (
...
<input
className={classNames({ 'invalid': !isEmailValid })}
type="email"
value={state.email}
onChange={handleChange}
/>
{!isEmailValid && 'Some error message'}
<button type="submit" disabled={!isFormValid} className="button">Enter</button>
...
);
...
Your validator hook can return validation message instead of boolean, like:
const useEmailValidation = (email) => {
if (!email || email.length === 0) {
return 'Email cannot be empty';
}
const isEmailValid = /#/.test(email); // use any validator you want
if (!isEmailValid) {
return 'Invalid email provided';
}
return null;
};
Also it is a good practice to show validation message only after field was focused before and after user tried to submit the form.
Formik is a great plugin that will help you perform form validation. The examples are also quite clear.
Or you could do something like this:
const Login = () => {
const [error, setError] = useState(null);
const [state, setState] = useState({
email: '',
password: '',
});
const validateEmail = (email) => {
const re =
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
};
const handleChange = (e) => {
const { id, value } = e.target;
setState((prevState) => ({
...prevState,
[id]: value,
}));
};
const handleSubmitClick = (e) => {
e.preventDefault();
if (!validateEmail(state.email)) {
setError('Invalid Email');
}
if (state.password.length < 8) {
setError('Password must be at least 8 chars long');
}
if (!error) {
// No errors.
}
};
return (
<>
<div className='container'>
<div className='title'>
{error && <div style={{ color: 'red' }}>{error}</div>}
<form onSubmit={handleSubmitClick}>
<div className='form-group'>
<input
type='email'
className='email'
placeholder='Email'
value={state.email}
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
type='password'
className='password'
placeholder='Password'
value={state.password}
onChange={handleChange}
/>
</div>
<button type='submit' className='button'>
Enter
</button>
</form>
</div>
</div>
</>
);
};
export default Login;
For an empty validation you can check it preventing the submit if the field is empty, like
const handleSubmitClick = (e) => {
e.preventDefault();
if(email.trim() === '' || password.trim() === ''){
//Add a h1 or section with the error message
}else{
console.log("Authenticated",state);
}
}
As long as the email field type is equal to email, which is your case, the browser should give an alert if the string is not an email. ("user#example.com")

How to write validations for email and password using react hooks

I am working on React in that project I am trying to write validations for Email and password by
using react hooks But I don't know how to start it write it so please help to achieve this.
What I want exactly is I hard coded Email and Password in my code. Now Just I want to write validations for Email and Password. What I want exactly is If I enter Incorrect Email and Correct password then in Validations it have to show only please enter valid email address.
If I enter correct Email then it has to show please enter correct password. If both are wrong means then it has to show validations please enter correct Email and Password.
This is my code
import React, { useState } from 'react';
import './Login.css';
const Login = () => {
const [loginData, setLoginData] = useState(null)
const loginCredentials = () => {
if (loginData.email === 'john#gmail.com' && loginData.password === 'christina') {
} else {
}
}
const handleChange = ({ target }) => {
const { name, value } = target
const newData = Object.assign({}, loginData, { [name]: value })
setLoginData(newData)
}
const handleSubmit = (e) => {
e.preventDefault()
loginCredentials()
}
return (
<div className='container'>
<div className='row justify-content-center'>
<div className='col-4'>
<div className='mainform mt-3'>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="email">Email</label>
<input type="email" name='email' className="form-control" id="email" onChange={handleChange}></input>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input type="password" name='password' className="form-control" id="password" onChange={handleChange}></input>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
)
}
export default Login
Try saving the errors in an array and check the length of array at the end.
const [errors,setErrors]=useState([]);
.
.
.
if(username !='john#gmail.com')
{
setErrors([...errors,{error:'Invalid username'}])
}
if(password!='something')
{
setErrors([...errors,{error:'Invalid password'}])
}
Now inside of the component
{errors?errors.map(elem=>elem.error):null}
You can create a state for the error message and change its value based on what combination of email and password is incorrect. If
const [errorMessage, setErrorMessage] = useState('')
const loginCredentials = () => {
if (loginData.email !== 'john#gmail.com' && loginData.password !== 'christina') {
setErrorMessage('email and pw are both incorrect')
} else if (loginData.email !== 'john#gmail.com' {
setErrorMessage('email is incorrect')
} else if {.....etc
}
then put {errorMessage} in your jsx where you want it to show up
You can use nested if else
const [error, setError] = useState("");
const loginCredentials = () => {
if (loginData.email === "john#gmail.com") {
if (loginData.password === "christina") {
setError("");
return true;
}
else
{
setError("Incorrect Password");
return false;
}
} else {
setError("Incorrect Email and password");
return false;
}
};
To show the error you can put a div somewhere in the form which will only show when there is error.
{error && <div>{error}</div>}
First of all, your input values are not controlled elements. learn about 'controlled component'.
There are lots of ways you can achieve this.
I think you can add a custom hook to handle onChange and reset handler.
custom hook:
import { useState } from 'react';
const useInputState = (initialValues) => {
const [state, setstate] = useState(initialValues);
const onChangeHandler = (e) => setstate({
...state,
[e.target.name]: e.target.value,
});
const reset = (field) => {
if (field) {
setstate({
...state,
[field]: initialValues[field],
});
} else {
setstate({
...initialValues,
});
}
};
return [state, onChangeHandler, reset];
};
export default useInputState;
Then consume that hook in your component.
import React, { useState } from 'react';
import UseInputState from '../hooks/useInputState';
const App = () => {
const [value, onChangeHandler, reset] = UseInputState('');
console.log({ value });
const [emailAlert, setEmailAlert] = useState(null);
const [passwordAlert, setPasswordAlert] = useState(null);
const loginCredentials = () => {
setEmailAlert(null);
setPasswordAlert(null);
const { email, password } = value;
if (email !== 'john#gmail.com') {
setEmailAlert('Please Enter valid Email Address');
}
if (password !== 'christina') {
setPasswordAlert(' please enter correct password');
}
reset('');
if (password === 'christina' && email === 'john#gmail.com') {
setEmailAlert(null);
setPasswordAlert(null);
}
};
const handleSubmit = (e) => {
e.preventDefault();
loginCredentials();
};
return (
<div className='container'>
<div className='row justify-content-center'>
<div className='col-4'>
<div className='mainform mt-3'>
<form onSubmit={handleSubmit}>
<div className='form-group'>
<label htmlFor='email'>Email</label>
<input
type='email'
name='email'
className='form-control'
id='email'
value={value.email || ''}
onChange={onChangeHandler}
></input>
</div>
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
type='password'
name='password'
className='form-control'
id='password'
value={value.password || ''}
onChange={onChangeHandler}
></input>
</div>
<button type='submit' className='btn btn-primary'>
Submit
</button>
</form>
{emailAlert && <div>{emailAlert}</div>}
{passwordAlert && <div>{passwordAlert}</div>}
</div>
</div>
</div>
</div>
);
};
export default App;

Random Component Behavior

I have 2 components, the problem is that on the first submit click i cant setUser(), (although addUser arguments are giving the correct values) it keeps the original state '', '', but if i click it again it change correctly. I don't know what I'm doing wrong, its my first question sorry if its poorly formatted.
import React, { useState, useEffect } from "react";
import "./notes.css";
import UserNameMailForm from "./userNameMailForm";
const NoteApp = props => {
const [user, setUser] = useState({
userName: "",
email: ""
});
const addUser = (userName, email) => {
const newUser = { userName, email };
setUser(newUser);
console.log(user);
console.log(userName, email);
};
return (
<div className="container p-0">
<div className="screen pt-2">
<p>Users</p>
</div>
<UserNameMailForm addUser={addUser} />
</div>
);
};
export default NoteApp;
The second component is this one:
import React, { useState, useEffect } from "react";
const UserNameMailForm = ({ addUser }) => {
const [userName, setUsername] = useState("");
const [email, setEmail] = useState("");
useEffect(() => {}, []);
const handleSubmit = e => {
e.preventDefault();
addUser(userName, email);
};
return (
<form onSubmit={handleSubmit} className="form-group">
<input
type="text"
className="form-control"
placeholder="User name"
value={userName}
onChange={e => setUsername(e.currentTarget.value)}
/>
<input
type="text"
className="form-control"
placeholder="email"
value={email}
onChange={e => setEmail(e.currentTarget.value)}
/>
<button type="submit" className="btn btn-outline-danger">
Add
</button>
</form>
);
};
export default UserNameMailForm;
You code is working fine, as this example demonstrates:
const { useState } = React;
const NoteApp = props => {
const [user, setUser] = useState({
userName: "",
email: ""
});
const addUser = (userName, email) => {
const newUser = { userName, email };
setUser(newUser);
};
return (
<div className="container p-0">
<div className="screen pt-2">
<p>Users</p>
{JSON.stringify(user)}
</div>
<UserNameMailForm addUser={addUser} />
</div>
);
};
const UserNameMailForm = ({ addUser }) => {
const [userName, setUsername] = useState("");
const [email, setEmail] = useState("");
const handleSubmit = e => {
e.preventDefault();
addUser(userName, email);
};
return (
<form onSubmit={handleSubmit} className="form-group">
<input
type="text"
className="form-control"
placeholder="User name"
value={userName}
onChange={e => setUsername(e.currentTarget.value)}
/>
<input
type="text"
className="form-control"
placeholder="email"
value={email}
onChange={e => setEmail(e.currentTarget.value)}
/>
<button type="submit" className="btn btn-outline-danger">
Add
</button>
</form>
);
};
ReactDOM.render(<NoteApp />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
The issue is that setUser is asynchronous, and user is a reference to the previous user object, which will be the object you pass as initial value to useState, so that's why console.log(user); is giving you the previous state.

Resources