How to activate button in condition - reactjs

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.

Related

mock function error in react-testing-library

Hii Everyone I am new to react testing ,I am trying to do some example for practise , I am getting a Error,Need your help , this is my App Component
const[firstName,setFirstName]=useState("")
const[lastName,setLastName]=useState("")
const [data,setData] = useState({})
const handleFirstName = (e) =>{
setFirstName(e.target.value)
}
const handleLastName = (e) =>{
setLastName(e.target.value)
}
const handleSubmit = (e) =>{
e.preventDefault();
setData({firstName,lastName})
console.log(firstName,lastName)
}
return (
<div className="App">
<form onSubmit={handleSubmit} data-testid="form" >
<div>
<label>FirstName
<input type="text" name="firstName" id="firstName" value={firstName} onChange={handleFirstName}/>
</label>
</div>
{firstName && firstName.length > 10 && <p data-testid="error-msg" >FirstName is not valid</p>}
<div>
<label>lastName
<input type="text" name="lastName" id="lastName" value={lastName} onChange={handleLastName}/>
</label>
</div>
<button type="submit" name="submit" disabled={firstName === ""} >submit</button>
</form>
</div>
);
}
this is my testing logic
const mockFunction = jest.fn();
const {getByText}=render(<App onSubmit={mockFunction}/>)
const firstNameLabel = screen.getByRole("textbox",{'name':'FirstName'})
fireEvent.change(firstNameLabel,{"target":{'value':"dhffssß"}})
const lastNameLabel = screen.getByRole("textbox",{"name":"lastName"})
fireEvent.change(lastNameLabel,{"target":{'value':"dhfffsß"}})
const btn = screen.getByRole('button',{'name':'submit'})
fireEvent.click(btn)
expect(mockFunction).toHaveBeenCalledTimes(1)
})
I am testing simple form but getting this error
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
Try to call onSubmit function which you pass in props in your handleSubmit handler. In this case you should be able to track if the onSubmit callback was called in your tests.
const App = ({ onSubmit }) => {
const[firstName,setFirstName]=useState("")
const[lastName,setLastName]=useState("")
const [data,setData] = useState({})
const handleFirstName = (e) =>{
setFirstName(e.target.value)
}
const handleLastName = (e) =>{
setLastName(e.target.value)
}
const handleSubmit = (e) =>{
e.preventDefault();
setData({firstName,lastName});
console.log(firstName,lastName);
onSubmit();
}
return (
<div className="App">
<form onSubmit={handleSubmit} data-testid="form" >
<div>
<label>FirstName
<input type="text" name="firstName" id="firstName" value={firstName} onChange={handleFirstName}/>
</label>
</div>
{firstName && firstName.length > 10 && <p data-testid="error-msg" >FirstName is not valid</p>}
<div>
<label>lastName
<input type="text" name="lastName" id="lastName" value={lastName} onChange={handleLastName}/>
</label>
</div>
<button type="submit" name="submit" disabled={firstName === ""} >submit</button>
</form>
</div>
);
}
}
You may need to wait until the submit button is enabled before trying to interact with it, after you changed the firstName input, and wait for the callback call (not sure the second wait is usefull) :
import { render, waitFor } from '#testing-library/react';
const mockFunction = jest.fn();
const {getByText}=render(<App onSubmit={mockFunction}/>)
const firstNameLabel = screen.getByRole("textbox",{'name':'FirstName'})
fireEvent.change(firstNameLabel,{"target":{'value':"dhffssß"}})
const lastNameLabel = screen.getByRole("textbox",{"name":"lastName"})
fireEvent.change(lastNameLabel,{"target":{'value':"dhfffsß"}})
// wait until button is enabled
await waitFor(() => expect(getByText('submit').toBeEnabled());
fireEvent.click(getByText('submit'));
await waitFor(() => expect(mockFunction).toHaveBeenCalled();

Change multiple elements simultaneously

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: "",
}
});
}

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.

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;

Resources