Formik / Yup Validation for Register Form on React - javascript

I am learning react and I have decided to create a React Front End that connects to Express.js on back-end. At the moment, I am trying to add validation on my register/login forms. I dont really understand how the validation system is implemented. I've noticed that tutorials use functional components for Formik/Yup however, I am using a class based component.
My register page doesn't display anything when i try to render it. Of course, I must have implemented this incorrectly, I cant figure out what to do. Thank you for help.
import React, { Component } from "react";
import { register } from "./UserFunctions";
import { Formik, Field } from "formik";
import * as Yup from "yup";
// const validationSchema = yup.object().shape({
// first_name: yup.string
// })
class Register extends Component {
constructor() {
super();
this.state = {
first_name: "",
last_name: "",
email: "",
password: "",
errors: {},
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
//new user object
const newUser = {
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
this.props.history.push(`/login`);
});
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: "",
}}
validationSchema={Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
})}
>
{(props) => {
const {
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
} = props;
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={this.state.first_name}
onChange={this.onChange}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">{errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={this.state.last_name}
onChange={this.onChange}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>;
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;

Formik validates the form when trying to submit, if there are any errors it doesn't allow the form to be submitted, it is upto you to display the error messages to the user when he tries to submit with errors see this page for reference

That is your code with functional component and with fewer code lines, it's working as expected
import React from "react";
import { useFormik } from "formik";
import { register } from "./UserFunctions";
import * as Yup from "yup";
const Register = ({ history }) => {
const validationSchema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number."),
});
const formik = useFormik({
initialValues: {
first_name: "",
last_name: "",
email: "",
password: "",
},
validationSchema: validationSchema,
onSubmit: (values) => {
const newUser = {
first_name: values.first_name,
last_name: values.last_name,
email: values.email,
password: values.password,
};
//if register function succesful, redirect to login page
register(newUser).then((res) => {
history.push(`/login`);
});
},
});
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={formik.handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={formik.values.first_name}
onChange={formik.handleChange}
/>
{formik.errors.first_name && formik.touched.first_name && (
<div className="input-feedback">{formik.errors.first_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={formik.values.last_name}
onChange={formik.handleChange}
/>
{formik.errors.last_name && formik.touched.last_name && (
<div className="input-feedback">{formik.errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={formik.values.email}
onChange={formik.handleChange}
/>
{formik.errors.email && formik.touched.email && (
<div className="input-feedback">{formik.errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={formik.values.password}
onChange={formik.handleChange}
/>
{formik.errors.password && formik.touched.password && (
<div className="input-feedback">{formik.errors.password}</div>
)}
</div>
<button type="submit" className="btn btn-lg btn-primary btn-block">
Register!
</button>
</form>
;
</div>
</div>
</div>
);
};
export default Register;

First, you need not use state while using Form Libraries. The main purpose of using Form libraries is to encapsulate the state and reduce the boilerplate.
Formik, itself, is a container for your form state.
Please Note :- you should pass the values of the formik form data into the input fields by using values ( which contains your form data) and handleChange provided by formik
Formik takes onSubmit as a prop where you can get your form state after validation and then, here you can register for your new user.
Also, you can check the codesandbox link here.
import React, { Component } from "react";
// import { register } from "./UserFunctions";
import { Formik } from "formik";
import * as Yup from "yup";
const schema = Yup.object().shape({
first_name: Yup.string()
.required("First Name is Required.")
.min(1, "First Name is Too Short."),
last_name: Yup.string()
.required("Last Name is Required.")
.min(1, "Last Name is Too Short."),
email: Yup.string().email().required("Email is Required."),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number.")
});
class Register extends Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<Formik
initialValues={{
first_name: "",
last_name: "",
email: "",
password: ""
}}
validationSchema={schema}
// tell the formik to validate onBlur
validateOnBlur
onSubmit={(values) => {
// here you have the access to the form data
// values.first_name, values_last_name, values_email, values_password
//if register function succesful, redirect to login page
// register(values).then((res) => {
// this.props.history.push(`/login`);
// })
}}
>
{(props) => {
const {
touched,
errors,
handleSubmit,
values,
handleChange,
handleBlur,
} = props;
return (
<form noValidate onSubmit={handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={values.first_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.first_name && touched.first_name && (
<div className="input-feedback">
{errors.first_name}
</div>
)}
</div>
<div className="form-group">
<label htmlFor="name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your last name"
value={values.last_name}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.last_name && touched.last_name && (
<div className="input-feedback">{errors.last_name}</div>
)}
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.email && touched.email && (
<div className="input-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
/>
{errors.password && touched.password && (
<div className="input-feedback">{errors.password}</div>
)}
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>
);
}}
</Formik>
</div>
</div>
</div>
);
}
}
export default Register;

Related

How to use react-phone-input-2 in form in React projects?

I want to implement a form with postal country code phone number and I am using the react-phone-input-2 library for this but when I click on the button submit I am getting an empty phone string in my console and I am using the default "us" postal country code in my input but all are coming. How to use all of the country postal and how to print its value in my console with all inputs of form. I am using here Formik and yup libraries for validation in my React project.
import "./FormInput.css";
import { Formik } from "formik";
import * as Yup from "yup";
import PhoneInput from "react-phone-input-2";
import "react-phone-input-2/lib/style.css";
const validate = Yup.object({
username: Yup.string()
.min(2, "Must be 2 chracters or more")
.max(15, "Must be 15 chracters or less")
.required("Username field is required!"),
email: Yup.string()
.email("Email is Invalid")
.required("Email field is required!"),
phone: Yup.string().max(15, "Phone number must be 15 or less"),
company: Yup.string().required("Company field is required!"),
website: Yup.string()
.matches(
/((https?):\/\/)?(www.)?[a-z0-9]+(\.[a-z]{2,}){1,3}(#?\/?[a-zA-Z0-9#]+)*\/?(\?[a-zA-Z0-9-_]+=[a-zA-Z0-9-%]+&?)?$/,
"Enter correct url!"
)
.required("Website field is required!"),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(
/^[a-zA-Z0-9!##$%^&*]{6,16}$/,
"password should contain atleast one number and one special character"
)
.required("Passowrd field is required!"),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password"), null])
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(
/^[a-zA-Z0-9!##$%^&*]{6,16}$/,
"password should contain atleast one number and one special character"
)
.required("Passowrd field is required!")
});
const FormInput = () => {
return (
<>
<Formik
initialValues={{
username: "",
email: "",
phone: "",
company: "",
website: "",
password: "",
confirmPassword: ""
}}
validationSchema={validate}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting
/* and other goodies */
}) => (
<form className="form" onSubmit={handleSubmit}>
<h2>Fill the Form</h2>
<div className="form-control">
<input
id="username"
type="username"
name="username"
onChange={handleChange}
onBlur={handleBlur}
value={values.username}
placeholder="Enter username"
/>
<p>{errors.username && touched.username && errors.username}</p>
</div>
<div className="form-control">
<input
id="email"
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
placeholder="Enter email"
/>
<p>{errors.email && touched.email && errors.email}</p>
</div>
<div className="form-control">
<PhoneInput
id="phone"
placeholder="Enter phone number"
onChange={handleChange}
onBlur={handleBlur}
value={values.phone}
type="text"
inputProps={{
name: "phone",
required: true,
autoFocus: true
}}
country={`us`}
inputClass="phone-input"
inputStyle={{
width: "100%",
height: "42px"
}}
/>
<p>{errors.phone && touched.phone && errors.phone}</p>
</div>
<div className="form-control">
<input
id="company"
type="text"
name="company"
onChange={handleChange}
onBlur={handleBlur}
value={values.company}
placeholder="Enter company name"
/>
<p>{errors.company && touched.company && errors.company}</p>
</div>
<div className="form-control">
<input
id="website"
type="url"
name="website"
onChange={handleChange}
onBlur={handleBlur}
value={values.website}
placeholder="Enter website url"
/>
<p>{errors.website && touched.website && errors.website}</p>
</div>
<div className="form-control">
<input
id="password"
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
placeholder="Enter password"
/>
<p>{errors.password && touched.password && errors.password}</p>
</div>
<div className="form-control">
<input
id="confirmPassword"
type="password"
name="confirmPassword"
onChange={handleChange}
onBlur={handleBlur}
value={values.confirmPassword}
placeholder="Enter confirm password"
/>
<p>
{errors.confirmPassword &&
touched.confirmPassword &&
errors.confirmPassword}
</p>
</div>
<button type="submit" className="btn" disabled={isSubmitting}>
Submit
</button>
</form>
)}
</Formik>
</>
);
};
export default FormInput;

How to trigger modal from a different component

1st component (here is the modal)
import './Register.css';
import {useFormik} from 'formik';
import * as Yup from 'yup';
import {useNavigate} from 'react-router-dom';
import {registerUser} from '../../services/auth.service';
import {
createUser,
getUserByUsername,
} from '../../services/user.request.services.js';
import {useContext} from 'react';
import AppContext from '../../context/AppContext.js';
export default function Register() {
const navigate = useNavigate();
const {addToast} = useContext(AppContext);
const formik = useFormik({
initialValues: {
username: '',
firstName: '',
lastName: '',
email: '',
password: '',
},
validationSchema: Yup.object({
username: Yup.string()
.max(50, 'Username must be maximum 50 symbols')
.required('Please, choose username.')
.min(4, 'username must be minimum 4 symbols'),
firstName: Yup.string()
.max(32, 'Last name must be maximum 32 symbols')
.min(4, 'First name must be minimum 4 symbols')
.required('You need to apply first name!'),
lastName: Yup.string()
.max(32, 'First name must be maximum 32 symbols')
.min(4, 'Last name must be minimum 4 symbols')
.required('You need to apply last name!'),
email: Yup.string()
.email('Must be a valid email address')
.required('Email is required!'),
password: Yup.string().required('Please, type your password.'),
}),
onSubmit: async (values) => {
try {
const user = await getUserByUsername(values.username);
if (user !== null) {
formik.errors.username = 'Username already in use!';
return;
}
const credentials = await registerUser(values.email, values.password);
try {
await createUser(
values.firstName,
values.lastName,
values.email,
values.username,
credentials.user.uid,
);
} catch (error) {
addToast('error', 'Something went wrong!');
}
} catch (error) {
if (error.message.includes('auth/email-already-in-use')) {
formik.errors.email = 'This email is already in use!';
return;
}
addToast('error', 'Something went wrong');
}
addToast('success', 'You have been registered!');
addToast('success', 'Welcome to the UnHOOman Forum!');
navigate('/');
},
});
const resetFormHandler = () => {
formik.resetForm();
};
return (
<>
<label htmlFor="register-modal">
<p className="cursor-pointer">Register</p>
</label>
<input type="checkbox" id="register-modal" className="modal-toggle" />
<div className="modal modal-bottom sm:modal-middle">
<div className="modal-box min-h-1/2">
<div className="modal-action m-0 justify-center gap-2 h-full border-2 border-[#F79E47]">
<form onSubmit={formik.handleSubmit} className="w-full">
<div className='form-wrapper'>
<h1 className="font-bold">Register</h1>
<input
type="text"
placeholder="Username..."
name="username"
value={formik.values.username}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input w-full input-bordered border-[#F79E47] max-w-xs block"
/>
{formik.touched.username && formik.errors.username ? (
<p className="text-red-700 text-sm">{formik.errors.username}</p>
) : null}
<input
type="text"
placeholder="First name..."
name="firstName"
value={formik.values.firstName}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.firstName && formik.errors.firstName ? (
<p className="text-red-700 text-sm">{formik.errors.firstName}</p>
) : null}
<input
type="text"
placeholder="Last name..."
name="lastName"
value={formik.values.lastName}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.lastName && formik.errors.lastName ? (
<p className="text-red-700 text-sm">{formik.errors.lastName}</p>
) : null}
<input
type="email"
placeholder="Email..."
name="email"
value={formik.values.email}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.email && formik.errors.email ? (
<p className="text-red-700 text-sm">{formik.errors.email}</p>
) : null}
<input
type="password"
placeholder="Password..."
name="password"
value={formik.values.password}
onBlur={formik.handleBlur}
onChange={formik.handleChange}
className="input input-bordered border-[#F79E47] w-full max-w-xs"
/>
{formik.touched.password && formik.errors.password ? (
<p className="text-red-700 text-sm">{formik.errors.password}</p>
) : null}
<div className="signup-buttons">
<label
type="button"
htmlFor="register-modal"
className="btn btn-outline"
onClick={() => resetFormHandler()}
>
CANCEL
</label>
<button type="submit" className="btn btn-outline">
Submit
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
}
2nd component from which I want to be able to trigger the modal
import {HOME_CARD_TITLE, HOME_CARD_DESC} from '../../common/constants.js';
import {useContext, useEffect, useState} from 'react';
import {getAllPosts} from '../../services/posts.service.js';
import {getAllUsers} from '../../services/user.request.services.js';
import AppContext from '../../context/AppContext.js';
import {Navigate, useNavigate} from 'react-router-dom';
export default function HomeInfoCard() {
const [cardState, setCardState] = useState({posts: 0, members: 0});
const {addToast} = useContext(AppContext);
const navigate = useNavigate();
useEffect(() => {
const updatePostsAndUsers = async () => {
const posts = (await getAllPosts()).length;
const members = (await getAllUsers()).length;
setCardState((prev) => ({
...prev,
posts,
members,
}));
};
updatePostsAndUsers().catch((error) => addToast('error', error.message));
}, []);
return (
<div className="card max-w-md bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">{ HOME_CARD_TITLE }</h2>
<p className="mb-3">{ HOME_CARD_DESC }</p>
<div className="stats shadow text-center mb-3">
<div className="stat">
<div className="stat-title">Posts</div>
<div className="stat-value">{ cardState.posts }</div>
</div>
<div className="stat">
<div className="stat-title">Members</div>
<div className="stat-value">{ cardState.members }</div>
</div>
</div>
<div className="card-actions justify-center">
<button className="btn bg-[#FCDDBF] border-0 text-[#4A4846] hover:text-[#fff] w-full" onClick={() => navigate('/login')}>Join community</button>
</div>
</div>
</div>
);
}
I have two components, the first one is a register form with Formik and there is also a modal from daisyUI.
I have a 'View community button' in other component which I want to be able to make on click to open the register form modal from the other component, but I couldn't find a solution for this problem.
I've tried with data-toggle/data-target, but it didn't work.
The button which I want to be able to trigger the modal from the register component is in the end of the second component and it's currently leading to the home page with useNavigate.
I am not a professional programmer, and currently I am in a boot camp, so that's why the code is trashy, but we all start from somewhere. Thanks in advance for the replies!

How can Prompt be used to stop navigation on formik dirty state

I'm using formik in a reactjs project, and I want to use Prompt from react-router to open a notification before a user leaves and loses changes made to their submission.
I'd expected something like this to work:
<Prompt
when={formik.dirty}
message="You have unsaved changes. Are you sure you want to leave?"
/>
My formik block looks like this:
const formik = useFormik({
initialValues: {
<values>
},
enableReinitialize: true,
validate,
onSubmit: values => {
<submit functional stuff>
}
});
And my form is something like this:
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
disabled={isDisabled}
/>
{formik.errors.name ? <div className="text-danger">{formik.errors.name}</div> : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
disabled={isDisabled}
/>
{formik.errors.subject ? <div className="text-danger">{formik.errors.subject}</div> : null}
</div>
</div>
</form>
but it appears that formik.dirty is either not defined or it's not seen as true (despite making changes to the form).
How would I properly use the dirty prop to trigger the Prompt?
I am not sure what kind of setup you have, but I created a PoC with routing which has two tabs (links) for navigation and I am using prompt on tab with formik form component.
import React from "react";
import { Prompt } from "react-router-dom";
import { useFormik } from "formik";
const MyForm = () => {
const formik = useFormik({
initialValues: {
name: "",
subject: ""
},
enableReinitialize: true,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
onChange: (e) => {
console.log(e);
}
});
return (
<div>
<Prompt
when={!!formik.dirty}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
/>
{formik.errors.name ? (
<div className="text-danger">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
/>
{formik.errors.subject ? (
<div className="text-danger">{formik.errors.subject}</div>
) : null}
</div>
{formik.dirty && <button tye="submit">Save</button>}
</div>
</form>
</div>
);
};
export default MyForm;
take a look the this codesandbox.

Conditionally render empty div or error with React & Bootstrap

I'm setting up a signup form that displays errors below the input fields if the user makes a mistake. The way I have it setup right now, the form will add a div with the error below when the user tries to submit their info. My issue is that when there's an error, it adds the div and messes up the layout of the form because it has to move everything to make space for each error. Is there a way to just have an empty div there if there isn't any errors so that it doesn't mess with the layout when there is one? So like, instead of having margin for spacing between fields, it's an empty div for the errors.
import React, { Component } from "react";
import axios from "axios";
import classnames from "classnames";
import "./Signup.css";
class Signup extends Component {
constructor() {
super();
this.state = {
username: "",
email: "",
password: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const newUser = {
username: this.state.username,
email: this.state.email,
password: this.state.password
};
axios
.post("api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
}
render() {
const { errors } = this.state;
return (
<div className="signup-form">
<form noValidate onSubmit={this.onSubmit}>
<h2>Sign Up</h2>
<p>It's free and only takes a minute.</p>
<hr />
<div className="form-group">
<label>Username</label>
<input
type="text"
name="username"
className={classnames("form-control form-control-md", {
"is-invalid": errors.username
})}
value={this.state.username}
onChange={this.onChange}
/>
{errors.username && (
<div className="invalid-feedback">{errors.username}</div>
)}
</div>
<div className="form-group">
<label>Email</label>
<input
type="text"
name="email"
className={classnames("form-control form-control-md", {
"is-invalid": errors.email
})}
value={this.state.email}
onChange={this.onChange}
/>
{errors.email && (
<div className="invalid-feedback">{errors.email}</div>
)}
</div>
<div className="form-group">
<label>Password</label>
<input
type="text"
name="password"
className={classnames("form-control form-control-md", {
"is-invalid": errors.password
})}
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block btn-lg">
Sign Up
</button>
</div>
<p className="small text-center">
By clicking the Sign Up button, you agree to our <br />
Terms & Conditions, and{" "}
Privacy Policy
</p>
<div className="text-center">
Already have an account? Login here
</div>
</form>
</div>
);
}
}
export default Signup;
Yes, you can use visibility:hidden property of css.
<div style={{ visibility: error.email? 'visible': 'hidden'}}></div>
since visibility always takes up space, in both cases it is visible as well as hidden. so it won't mess with the layout.

Make Invalid Validation Disappear After 5 Seconds --ReactJS--

I'm trying to make the invalid feedback validation disappear after being on the screen for 5 seconds. in my state i have an empty errors object, when the form is submitted, the api call catches any errors from the backend, and they are placed in the errors object, the inputs use conditionals based on the errors object to show the validation. I've tried creating a setTimeout function that sets the state to an empty object after 5 seconds, but this causes breaking glitches if the form is submitted again incorrectly. Any insights how I can do this?
Register.js
import React, { Component } from "react";
import axios from 'axios';
import classnames from 'classnames';
class Register extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
password: "",
password2: "",
errors: {}
};
this.onChange = this.onChange.bind(this);
}
onChange(e) {
// THIS FUNCTION MUST BE BOUND -SEE ABOVE BIND
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
// ARROW FUNCTIONS DO NOT NEED TO BE BOUND
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
};
this.setState({
email: "",
name: "",
password: "",
password2: ""
});
axios
.post("/api/users/register", newUser)
.then(res => console.log(res.data))
.catch(err => this.setState({ errors: err.response.data }));
};
render() {
const { errors } = this.state;
return (
<div>
<div className="register">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h1 className="display-4 text-center text-dark">Sign Up</h1>
<p className="lead text-center">Create your DevMuse account</p>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<input
type="text"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.name
})}
placeholder="Name"
name="name"
value={this.state.name}
onChange={this.onChange}
/>
{errors.name && (
<div className="invalid-feedback">{errors.name}</div>
)}
</div>
<div className="form-group">
<input
type="email"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.email
})}
placeholder="Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
/>
{errors.email ? (
<div className="invalid-feedback">{errors.email}</div>
) : (
<small className="form-text text-muted text-center">
This site uses Gravatar so if you want a profile image,
use a Gravatar email
</small>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password
})}
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.onChange}
/>
{errors.password && (
<div className="invalid-feedback">{errors.password}</div>
)}
</div>
<div className="form-group">
<input
type="password"
className={classnames("form-control form-control-lg", {
"is-invalid": errors.password2
})}
placeholder="Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
/>
{errors.password2 && (
<div className="invalid-feedback">{errors.password2}</div>
)}
</div>
<input
type="submit"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Register;
Just clear any existing timeout before you initiate a new timeout:
componentWillUnmount() {
clearTimeout(this.clearError);
}
...
.catch((err) => {
this.setState({ errors: err.response.data });
clearTimeout(this.clearError); // clear previous timeout, if exists
this.clearError = setTimeout(() => {
this.setState({ errors: {} });
}, 5000);
});

Categories

Resources