Here is my code. The confirm state variable on line 34 will not change. Calling setConfirm from handleSubmit does not change it. I've attempted deliberately toggling it in dev tools, no effect. I've tried deliberately setting it to true in the code, and placing {confirm && <h1>TEST</h1>} at the top of the contact form. It doesn't render the h1, even when deliberately set to true. But {true && <h1>TEST</h1>} works just fine. It's as if the confirm variable doesn't even exist. Can someone shed some light on why this might be happening?
import { useState, useEffect } from 'react'
import styles from "./ContactAndBooking.module.css"
const ContactAndBooking = () => {
// Contact form values
const [contact, setContact] = useState({
name: null,
email: null,
phone: null,
reason: null,
message: null
})
// Booking form values
const [booking, setBooking] = useState({
name: null,
email: null,
phone: null,
date: null,
time: null,
description: null
})
// Store today's date, calendar selection minimum
const [minDate, setMinDate] = useState()
// Display contact form or booking form, switch on button press
const [typeContact, setTypeContact] = useState("contact")
// Flag invalid form values for validation alerts
const [validFlag, setValidFlag] = useState(true)
// Successful message confirmation flag
const [confirm, setConfirm] = useState(false)
// Keep track of today's date, booking calendar selection minimum
useEffect(()=> {
let today = new Date
let dd = today.getDate()
let mm = today.getMonth() + 1
let yyyy = today.getFullYear()
if (dd < 10)
dd = '0' + dd
if (mm < 10)
mm = '0' + mm
today = yyyy + '-' + mm + '-' + dd
setMinDate(today)
})
// Validate form values
const validate = () => {
// Track and return form validity, switch to false if any field fails
let isValid = true
// Check validity of contact form values
if (typeContact === "contact"){
// If no name, trigger invalid flag
if (!contact.name){
isValid = false
}
// If no email or email does not match regex, trigger invalid flag
if (!contact.email || !contact.email.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)){
isValid = false
}
// If phone number length is neither 10 nor 11 digits long, trigger invalid flag
if (!contact.phone || ![10,11].includes(contact.phone.split('-').join('').split('').length) || contact.phone.split('-').join('').split('').some(e => !'0123456789'.includes(e))){
isValid = false
}
// If no reason for contact is given, trigger invalid flag
if (!contact.reason){
isValid = false
}
// If message field is blank, trigger invalid flag
if (!contact.message){
isValid = false
}
}
// Check validity of booking form values
else if (typeContact === "booking"){
// If no name, trigger invalid flag
if (!booking.name){
isValid = false
}
// If no email or email does not match regex, trigger invalid flag
if (!booking.email || !booking.email.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)){
isValid = false
}
// If phone number length is neither 10 nor 11 digits long, trigger invalid flag
if (!booking.phone || ![10,11].includes(booking.phone.split('-').join('').split('').length)|| booking.phone.split('-').join('').split('').some(e => !'0123456789'.includes(e))){
isValid = false
}
// If no booking date is selected, trigger invalid flag
if (!booking.date){
isValid = false
}
// If no booking time is selected, trigger invalid flag
if (!booking.time){
isValid = false
}
// If no booking description is given, trigger invalid flag
if (!booking.description){
isValid = false
}
}
// set form validation alert flag on validation failure
!isValid ? setValidFlag(false) : setValidFlag(true)
return isValid
}
const handleSubmit = (e) => {
e.preventDefault()
setValidFlag(true)
if (validate()){
console.log("CONFIRMATION TRIGGERING...") // TESTING
// Trigger confirmation window
setConfirm(true) // NOT TRIGGERING
console.log("CONFIRMATION TRIGGERED") // TESTING
if (typeContact === "contact"){
// To be replaced with Axios request
console.log(contact)
}
else if (typeContact === "booking"){
// To be replaced with Axios request
console.log(booking)
}
}
// set form validation alert flag on validation failure
else {
setValidFlag(false)
console.log("SUBMISSION INVALID")
}
}
// Contact form view
const contactWindow = () => {
return (
<form onSubmit={e => handleSubmit(e)}>
{confirm && <h1>TEST</h1>}
<label className={styles.label} htmlFor="name">Name: </label>
<input
className={styles.input}
name="name"
type="text"
onChange={e => setContact({...contact, name: e.target.value})}
/>
<span className={!validFlag && !contact.name ? styles.warning : styles.warningHidden}>Please enter your name.</span>
<label className={styles.label} htmlFor="email">Email: </label>
<input
className={styles.input}
name="email"
type="email"
onChange={e => setContact({...contact, email: e.target.value})}
/>
<span className={
!validFlag && (!contact.email || !contact.email.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/))
? styles.warning
: styles.warningHidden
}>
Please enter a valid email address.
</span>
<label className={styles.label} htmlFor="phone">Phone: </label>
<input
className={styles.input}
name="phone"
type="text"
onChange={e => setContact({...contact, phone: e.target.value})}
/>
<span className={
!validFlag && (!contact.phone || ![10,11].includes(contact.phone.split('-').join('').split('').length) || contact.phone.split('-').join('').split('').some(e => !'0123456789'.includes(e)))
? styles.warning
: styles.warningHidden
}>Please enter a valid phone number.</span>}
<select
className={styles.select}
onChange={e => setContact({...contact, reason: e.target.value})}
defaultValue="Reason for contact"
>
<option className={styles.option} value="Reason for contact" disabled>Reason for contact</option>
<option className={styles.option} value="Business Inquiry">Business Inquiry</option>
<option className={styles.option} value="Ticket Sales">Ticket Sales</option>
<option className={styles.option} value="Other">Other</option>
</select>
<span className={!validFlag && !contact.reason ? styles.warning : styles.warningHidden}>Please select a reason for contact.</span>
<textarea
className={`${styles.textarea} ${styles.textareaContact}`}
placeholder="How can I help you?"
onChange={e => setContact({...contact, message: e.target.value})}
/>
<span className={!validFlag && !contact.message ? styles.warning : styles.warningHideen}>Please enter your message.</span>
<button className={styles.submit} role="submit">Submit</button>
</form>
)
}
// Booking form view
const bookingWindow = () => {
return (
<form onSubmit={handleSubmit}>
<label className={styles.label} htmlFor="name">Name: </label>
<input
className={styles.input}
name="name"
type="text"
onChange={e => setBooking({...booking, name: e.target.value})}
/>
<span className={!validFlag && !booking.name ? styles.warning : styles.warningHidden}>Please enter your name.</span>
<label className={styles.label} htmlFor="email">Email: </label>
<input
className={styles.input}
name="email"
type="email"
onChange={e => setBooking({...booking, email: e.target.value})}
/>
<span className={
!validFlag && (!booking.email || !booking.email.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/))
? styles.warning
: styles.warningHidden
}>
Please enter a valid email address.
</span>
<label className={styles.label} htmlFor="phone">Phone: </label>
<input
className={styles.input}
name="phone"
type="text"
onChange={e => setBooking({...booking, phone: e.target.value})}
/>
<span className={
!validFlag && (!booking.phone || ![10,11].includes(booking.phone.split('-').join('').split('').length)|| booking.phone.split('-').join('').split('').some(e => !'0123456789'.includes(e)))
? styles.warning
: styles.warningHidden
}>
Please enter a valid phone number.
</span>
<label className={styles.label} htmlFor="date">Select Date: </label>
<input
className={styles.datetime}
type="date"
name="date"
min={minDate}
onChange={e => setBooking({...booking, date: e.target.value})}
/>
<span className={!validFlag && !booking.date ? styles.warning : styles.warningHidden}>Please select a date for your event.</span>
<label className={styles.label} htmlFor="time">Select Time: </label>
<input
className={styles.datetime}
type="time"
name="time"
onChange={e => setBooking({...booking, time: e.target.value})}
/>
<span className={!validFlag && !booking.time ? styles.warning : styles.warningHidden}>Please select a time for your event.</span>
<textarea
className={`${styles.textarea} ${styles.textareaBooking}`}
placeholder="Please give a brief description of your event."
onChange={e => setBooking({...booking, description: e.target.value})}
/>
<span className={!validFlag && !booking.description ? styles.warning : styles.warningHidden}>Please describe your event.</span>
<button className={styles.submit} role="submit">Submit</button>
</form>
)
}
// Message confirmation window, triggered on successful validation and submission
const confirmWindow = () => {
return (
<div className={styles.confirmContainer}>
<h1 className={`${styles.confirmText} ${styles.confirmThankYou}`}>Thanks for the message!</h1>
<h3 className={`${styles.confirmText} ${styles.confirmSubThankYou}`}>I'll get back to you ASAP.</h3>
<button className={styles.confirmOkay} onClick={setConfirm(false)}>Okay</button>
</div>
)
}
return (
<div className={styles.container} id="contact">
<button
role="button"
className={`${styles.toggle} ${styles.toggleContact}`}
onClick={() => {
setTypeContact("contact")
setValidFlag(true)
}}
>
CONTACT
</button>
<button
role="button"
className={`${styles.toggle} ${styles.toggleBooking}`}
onClick={() => {
setTypeContact("booking")
setValidFlag(true)
}}
>
BOOKING
</button>
<br />
{typeContact === "contact" && contactWindow()}
{typeContact === "booking" && bookingWindow()}
{confirm && confirmWindow()}
</div>
)
}
export default ContactAndBooking
When you call setConfirm(false) in your onClick event, what you are actually saying is to run the setConfirm state update as soon as the page loads. What you want to do is create an arrow function in the onClick so that you are passing reference to a function that will be ran once the button is clicked.
<button className={styles.confirmOkay} onClick={() => setConfirm(false)}>Okay</button>
If you wanted to do more than just call setConfirm, you can create another function and call setConfirm inside of that, then you can just pass the new function as reference so that every time the button is clicked the setConfirm function gets called.
const handleConfirmUpdate = () => {
// do some stuff in here if you'd like
setConfirm(true);
}
<button className={styles.confirmOkay} onClick={handleConfirmUpdate}>Okay</button>
Notice that we are not calling the function, but rather passing a reference.
try changing line 273 from
<button className={styles.confirmOkay} onClick={setConfirm(false)}>Okay</button>
to
<button className={styles.confirmOkay} onClick={() => setConfirm(false)}>Okay</button>
then it appears to be working
Related
there is required section in my form also I want to send successfully text after click of submit button. But problem is here when I click submit button, it shows successfully text no matter form is correct or not. Can you help me about that ? I am beginner :)
My react code here
import React, { Component } from "react";
export default class Form extends Component {
state = {
name: "",
surname: "",
phone: "",
email: "",
comments: "",
// isValid: true,
succesfully: ""
};
/* preventSending = async (e) => {
await this.setState({ [e.target.name]: e.target.value });
if (
this.state.name === "" ||
this.state.surname === "" ||
this.state.phone === "" ||
this.state.email === "" ||
this.state.comments === ""
) {
this.setState({ isValid: true });
} else {
this.setState({ isValid: false });
}
};*/
handleSubmit = (e) => {
this.setState({
succesfully: `${this.state.name} you have sent successfully `
});
};
render() {
return (
<div className="">
<form>
<label htmlFor="name">
Name :
<input
onChange={this.preventSending}
id="name"
name="name"
type="text"
required
/>
</label>
<br />
<label htmlFor="surname">
Surname :
<input
onChange={this.preventSending}
id="surname"
name="surname"
type="text"
required
/>
</label>
<br />
<label htmlFor="phone">
Phone :
<input
onChange={this.preventSending}
id="phone"
name="phone"
type="tel"
required
/>
</label>
<br />
<label htmlFor="email">
Email :
<input
onChange={this.preventSending}
id="email"
name="email"
type="email"
required
/>
</label>
<br />
<label htmlFor="textArea">
Comments :
<textarea
onChange={this.preventSending}
id="textarea"
name="comments"
required
/>
</label>
<br />
<button
type="submit"
// disabled={this.state.isValid}
onClick={this.handleSubmit}
>
Send details{" "}
</button>
</form>
<p>{this.state.succesfully}</p>
</div>
);
}
}
It says I have to add more details but I explained everything clear.
I have to go through your code and try to resolve the errors and get the proper output.
I see that you take the direct state object and update its value, I just corrected that part and also add one error flag in it, so that you can display one informational error message while you click the button without adding the data.
Apart from that, in your form, you take one button which has submit type.
As of now I simply update it with type=button, as type=submit will submit the form and redirect us to new URL.
Please let me know if it is useful to you or not.
here is my code
import React, { Component } from "react";
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
surname: "",
phone: "",
email: "",
comments: "",
// isValid: true,
succesfully: "",
error: "",
};
}
/* preventSending = async (e) => {
await this.setState({ [e.target.name]: e.target.value });
if (
this.state.name === "" ||
this.state.surname === "" ||
this.state.phone === "" ||
this.state.email === "" ||
this.state.comments === ""
) {
this.setState({ isValid: true });
} else {
this.setState({ isValid: false });
}
};*/
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value,
});
}
handleSubmit = (e) => {
if (
this.state.name !== "" &&
this.state.surname !== "" &&
this.state.phone !== "" &&
this.state.email !== "" &&
this.state.comments !== ""
) {
// check valid email or not with regex
const regexp = /^([\w\.\+]{1,})([^\W])(#)([\w]{1,})(\.[\w]{1,})+$/;
let isValidEmail = regexp.test(this.state.email) ? true : false;
if (isValidEmail) {
this.setState({
succesfully: `${this.state.name} you have sent successfully `,
error: "",
});
} else {
this.setState({
succesfully: "",
error: "Please add proper email",
});
}
} else {
this.setState({
succesfully: "",
error: "Please add proper data",
});
}
};
render() {
return (
<div className="">
<form>
<label htmlFor="name">
Name :
<input
onChange={(e) => this.handleChange(e)}
id="name"
name="name"
type="text"
value={this.state.name}
required
/>
</label>
<br />
<label htmlFor="surname">
Surname :
<input
onChange={(e) => this.handleChange(e)}
id="surname"
name="surname"
type="text"
value={this.state.surname}
required
/>
</label>
<br />
<label htmlFor="phone">
Phone :
<input
onChange={(e) => this.handleChange(e)}
id="phone"
name="phone"
type="tel"
value={this.state.phone}
required
/>
</label>
<br />
<label htmlFor="email">
Email :
<input
onChange={(e) => this.handleChange(e)}
id="email"
name="email"
type="email"
value={this.state.email}
required
/>
</label>
<br />
<label htmlFor="textArea">
Comments :
<textarea
onChange={(e) => this.handleChange(e)}
id="textarea"
name="comments"
value={this.state.comments}
required
/>
</label>
<br />
<button
// type="submit" // use this while you want to submit your form
type="button" // I use button to call handleSubmit method and display message
// disabled={this.state.isValid}
onClick={this.handleSubmit}
>
Send details
</button>
</form>
<p>{this.state.succesfully}</p>
<p>{this.state.error}</p>
</div>
);
}
}
I see that the function prevent sending that you created should work except you misplaced the isValid argument:
function ValidateEmail(email)
{
if (/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email))
{
return true
}
return false
}
if (
this.state.name === "" ||
this.state.surname === "" ||
this.state.phone === "" ||
this.state.email === "" ||
this.state.comments === "" ||
!validateEmail(this.state.email)
) {
this.setState({ isValid: false });
} else {
this.setState({ isValid: true });
}
also uncomment disabled and isValid which should be false at the begginning:
// disabled={this.state.isValid}
// isValid = false
I'm building a portal where students from only 6 universities can participate so I need a way to limit the email address type to these 6 universities for example emails ending with #mit.edu
How do I implement this in React with the form validation set to characters and on clicking on of these formats from the dropdown the email is saved as such for example in the email input field if you write cassandra and select #mit.edu from the dropdown the email becomes cassandra#mit.edu
This is how my login component looks like
`
function LogIn() {
const [password, setPassword] = React.useState("");
const [email, setEmail] = React.useState("");
const [err, setErr] = React.useState(null);
const history = useHistory();
const handleSubmit = async (event, password, email) => {
event.preventDefault();
var myHeaders = new Headers();
myHeaders.set('Authorization', 'Basic ' + encode(email + ":" + password));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let response;
try {
response = await fetch (`${APIlink}/users`, requestOptions)
} catch (err) {
setErr("Incorrect Password. Please Retry.");
return;
}
const result = await response.text();
//console.log(result);
const json = JSON.parse(result);
//console.log(json);
//console.log(response);
if (response.status===200) {
setErr(null);
localStorage.setItem('isAuthenticated', true);
//context.updateToken(JSON.stringify(json.data));
history.push("/Projects");
} else {
setErr(json.error);
console.log(json.error);
}
};
return (
<div>
<Header />
<div className="register row justify-content-center align-items-center">
<div className = "w-50 p-3">
<h1>Log in</h1>
<Link to="/Register">or create a new account</Link>
<br></br>
<br></br>
<form>
<div className="input-group mb-3">
<input type="text" id="email"
value={email}
onChange= {(event) => {
setErr("");
setEmail(event.target.value); }}
className="form-control form-control-lg" placeholder=" institute email" aria-label="institute-email" aria-describedby="basic-addon2"/>
</div>
<small
style={{ color: "red", height: "10px", display: "inline-block" }}
>
{err == "user not found" ? "Account doesn't exist" : ""}
</small>
<div className="input-group mb-3">
<input type="text" id="password"
value={password}
onChange={(event) => {
setPassword(event.target.value);
setErr("");
}}
className="form-control form-control-lg" placeholder="password" aria-label="password" aria-describedby="basic-addon2"/>
</div>
<small
style={{ color: "red", height: "10px", display: "inline-block" }}
>
{err == "incorrect password"
? "Incorrect Password"
: err == "Username and Password can't be empty"
? err
: ""}
</small>
<Submit
//loginState={loginState}
onClick={(event) => {
event.preventDefault();
if (email != "" && password != "") {
handleSubmit(event, password, email);
} else {
setErr("Username and Password can't be empty");
}
}}
>
{" "}
Log In
</Submit>
</form>
</div>
</div>
</div>
)
}
export default LogIn;
`
You can use regex to validate email. Add this validateEmail function right after clicking on submit and pass the email that you want to validate, then proceed further only if it returns true (it will return true if it matches the pattern).
function validateEmail(email)
{
var regex = /^[^\s#]+#mit\.edu$/;
var result = regex.test(email);
if(result == true){
//Proceed further
}
else{
console.log("Enter correct email address!")
}
}
According to this regex, it will return true only if there is exact '#mit.edu' right after any string. Like:-
'anystring#mit.edu' //Returns true
'anystring#mit.com' //Returns false
'anystring#mit.edu.com' //Returns false
You can do this similarly with other domains just replace mit.edu with any other domain or if the domain has more length then simply add '\.domain' for example: -
var regex = /^[^\s#]+#mit\.edu\.com$/;
This will return true if email is 'anystring#mit.edu.com'.
EDIT: A slight correction. Remove '+' after mit and edu, as it returns true on these conditions: -
anystring#mitt.edu
anystring#mit.eduu
Check the regex above now, it should work perfectly.
I am working on a form that is made with Formik, Yup, and ReactJS. In the date field, I am trying to validate if the user is 18 years old. I have passed the following as validationSchema paremeter in Formik:
import differenceInYears from "date-fns/differenceInYears";
...
...
...
dob: Yup.date()
.nullable()
.test("dob", "Should be greater than 18", function (value) {
return differenceInYears(value, new Date()) >= 18;
}),
The name of the formik input field is dob. But it shows the validation error even if I enter a valid date which is 18 years old. So, how to validate it properly?
You need to swap the date params:
differenceInYears(new Date(), new Date(value)) >= 18;
If you check date-fns docs, the first argument should be the later date.
Also you need to parse the field value into a Date.
My solution:
import React from "react";
import { Formik, Form, Field } from "formik";
import * as Yup from "yup";
import s from "./Registration.module.css";
const SignupSchema = Yup.object().shape({
firstName: Yup.string().required("Required"),
lastName: Yup.string().required("Required"),
birthdate: Yup.date()
.max(new Date(Date.now() - 567648000000), "You must be at least 18 years")
.required("Required"),
password: Yup.string()
.min(4, "Too Short!")
.max(50, "Too Long!")
.required("Required"),
email: Yup.string().email("Invalid email").required("Required"),
});
export const RegistrationForm = () => (
<div className={s.mainLoginForm}>
<h1>Sign up</h1>
<Formik
initialValues={{
firstName: "",
lastName: "",
email: "",
password: "",
birthdate: "",
}}
validationSchema={SignupSchema}
onSubmit={(values) => {
// same shape as initial values
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<div className={s.inputsFlex}>
<Field
className={s.regInput}
placeholder="email"
name="email"
type="email"
/>
{errors.email && touched.email ? (
<div className={s.formControl}>{errors.email}</div>
) : null}
<Field
className={s.regInput}
placeholder="password"
name="password"
type="password"
/>
{errors.password && touched.password ? (
<div className={s.formControl}>{errors.password}</div>
) : null}
<Field
className={s.regInput}
placeholder="firstName"
name="firstName"
/>
{errors.firstName && touched.firstName ? (
<div className={s.formControl}>{errors.firstName}</div>
) : null}
<Field
className={s.regInput}
placeholder="lastName"
name="lastName"
/>
{errors.lastName && touched.lastName ? (
<div className={s.formControl}>{errors.lastName}</div>
) : null}
<Field className={s.regInput} name="birthdate" type="date" />
{errors.birthdate && touched.birthdate ? (
<div className={s.formControl}>{errors.birthdate}</div>
) : null}
<button className={s.regBtn} type="submit">
Sign up
</button>
</div>
</Form>
)}
</Formik>
</div>
);
You can add 4 days for leap (intercalary) year
86 400 000 * 4 = 345 600 000
so if you want to do this without adding the date-fns plugin and dont want to use a mgic number of days then u can do something like this.
dob: Yup.date().nullable()
.test('dob', 'Should be greater than 18', function (value, ctx) {
const dob = new Date(value);
const validDate = new Date();
const valid = validDate.getFullYear() - dob.getFullYear() >= 18;
return !valid ? ctx.createError() : valid;
})
.required('Required'),
I have the following React Component, which holds a form with two inputs and a button.
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null
}
}
emailInputChanged = (e) => {
this.setState({
email: e.target.value.trim()
});
};
passwordInputChanged = (e) => {
this.setState({
password: e.target.value.trim()
});
};
loginButtonClicked = (e) => {
e.preventDefault();
if (!this.isFormValid()) {
//Perform some API request to validate data
}
};
signupButtonClicked = (e) => {
e.preventDefault();
this.props.history.push('/signup');
};
forgotPasswordButtonClicked = (e) => {
e.preventDefault();
this.props.history.push('/forgot-password');
};
isFormValid = () => {
const {email, password} = this.state;
if (email === null || password === null) {
return false;
}
return isValidEmail(email) && password.length > 0;
};
render() {
const {email, password} = this.state;
return (
<div id="login">
<h1 className="title">Login</h1>
<form action="">
<div className={(!isValidEmail(email) ? 'has-error' : '') + ' input-holder'}>
<Label htmlFor={'loginEmail'} hidden={email !== null && email.length > 0}>email</Label>
<input type="text" className="input" id="loginEmail" value={email !== null ? email : ''}
onChange={this.emailInputChanged}/>
</div>
<div className={(password !== null && password.length === 0 ? 'has-error' : '') + ' input-holder'}>
<Label htmlFor={'loginPassword'}
hidden={password !== null && password.length > 0}>password</Label>
<input type="password" className="input" id="loginPassword"
value={password !== null ? password : ''}
onChange={this.passwordInputChanged}/>
</div>
<button type="submit" className="btn btn-default" id="loginButton"
onClick={this.loginButtonClicked}>
login
</button>
</form>
<div className="utilities">
<a href={'/signup'} onClick={this.signupButtonClicked}>don't have an account?</a>
<a href={'/forgot-password'} onClick={this.forgotPasswordButtonClicked}>forgot your password?</a>
</div>
</div>
)
}
}
export function isValidEmail(email) {
const expression = /\S+#\S+/;
return expression.test(String(email).toLowerCase());
}
The values of the inputs are stored in the component's state. I have given them initial value of null and update them using setState() in the onChange event.
In the render() method I use the state to colour inputs with invalid values. Currently I am only checking the email value against a simple regex and the password to be at least 1 character in length.
The reason I have set the initial values of the state variables to null so I can check in the layout and the initial style on the input to not be marked as "has-errors". However I need to extend the check to:
this.state.email !== null && this.state.email.length === 0
in order to work, because null has no length property.
Is there a cleaner and "React-ish" way to achieve this:
- initial state of the div holding the inputs has no class has-errors
- less checks when setting the value attribute on the input (because React doesn't accept null as value of the attribute)
Edit:
If the initial value of this.state.email and this.state.password are empty strings, I get has-error applied to the div holding the inputs. In my opinion this is bad UX, because the user hasn't done anything and he is already wrong.
The hidden attribute is used by a custom component I made, which acts like "placeholder" (gets erased if something is typed in the input).
Video below showing how my form looks like when this.state.email and this.state.password are empty strings as well how my <Label> component works:
You could create a validate function which return an errors object to know which fields are in error, and use empty strings as initial values. I don't understand what you are trying to do with the hidden attribute.
Edit: add a touched property in the state, to know which field have been touched.
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
touched: {},
};
}
emailInputChanged = e => {
this.setState({
email: e.target.value.trim(),
touched: {
...this.state.touched,
email: true,
},
});
};
passwordInputChanged = e => {
this.setState({
password: e.target.value.trim(),
touched: {
...this.state.touched,
password: true,
},
});
};
loginButtonClicked = e => {
e.preventDefault();
if (!this.isFormValid()) {
//Perform some API request to validate data
}
};
isFormValid = () => {
const errors = this.validate();
return Object.keys(errors).length === 0;
};
validate = () => {
const errors = {};
const { email, password } = this.state;
if (!isValidEmail(email)) {
errors.email = true;
}
if (password.length === 0) {
errors.password = true;
}
return errors;
};
render() {
const { email, password, touched } = this.state;
const errors = this.validate();
return (
<div id="login">
<h1 className="title">Login</h1>
<form action="">
<div
className={
(errors.email && touched.email ? 'has-error' : '') +
' input-holder'
}
>
<Label htmlFor={'loginEmail'}>email</Label>
<input
type="text"
className="input"
id="loginEmail"
value={email}
onChange={this.emailInputChanged}
/>
</div>
<div
className={
(errors.password && touched.password ? 'has-error' : '') +
' input-holder'
}
>
<Label htmlFor={'loginPassword'}>password</Label>
<input
type="password"
className="input"
id="loginPassword"
value={password}
onChange={this.passwordInputChanged}
/>
</div>
<button
type="submit"
className="btn btn-default"
id="loginButton"
onClick={this.loginButtonClicked}
>
login
</button>
</form>
</div>
);
}
}
export function isValidEmail(email) {
const expression = /\S+#\S+/;
return expression.test(String(email).toLowerCase());
}
i am using number type here so that it accepts only number.But i don't want this number type like it's gives increments and decrements selector at the right side of the field.What i need is i need to accept only numbers not any characters.How can i do that?
these are my form elements:
<label className="w3-label w3-text-blue w3-large">
Phone Number:
<input type="number" className="w3-input w3-border" value={this.state.phoneno}
onChange={e => this.setState({ phoneno: e.target.value })} required placeholder="Enter Father's Phone No. in Numbers"/>
</label><br/>
<label className="w3-label w3-text-blue w3-large">
Mobile Number:
<input type="number" className="w3-input w3-border" value={this.state.mobileno} pattern="[0-9]{10}" title="Please enter 10 digit mobile Number"
onChange={e => this.setState({ mobileno: e.target.value })} required placeholder="Enter Father's Mobile No. in Numbers"/>
</label></div><br/>
Try this
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
https://css-tricks.com/snippets/css/turn-off-number-input-spinners/
You can use regular expressions in javascript to check whether given number is valid or not.
function phonenumber(inputtxt)
{
var phoneno = /^\d{10}$/;
if((inputtxt.value.match(phoneno))
{
return true;
}
else
{
alert("message");
return false;
}
}
This kind of tasks is where React really shines. Just use <input>s with type="text" and let your onChange handlers do the job. I would suggest using String.replace with \D (not a digit) regexp to filter out unwanted chars, and then String.substr to control the length of the entered value. Something like:
const filterNonDigits = value => value ? value.replace(/\D+/, '') : '';
const takeLast = (amount, value) => value ? value.substr(0, amount) : '';
class NumbersInput extends React.Component {
constructor() {
super();
this.state = {};
}
handlePhone(value) {
this.setState({ phoneno: filterNonDigits(value) });
}
handleMobile(value) {
this.setState({ mobileno: takeLast(10, filterNonDigits(value)) });
}
render() {
return (
<div>
<label>
Phone Number:
<input
value={this.state.phoneno}
onChange={e => this.handlePhone(e.target.value)}
/>
</label>
<br/>
<label>
Mobile Number:
<input
value={this.state.mobileno}
onChange={e => this.handleMobile(e.target.value)}
/>
</label>
</div>
);
}
}