I'm quite unsure how to do a form validation within react.
I want to display an error message on the current page saying. However, the password validation is ignored so their is no error that shows on the page.
Password must be at least characters
Maybe i'm not using conditional rendering right
SignUp.js (snippet for demonstration purpose)
constructor(props){
super(props);
this.state ={
errors: {},
}
handleSubmit(event) {
event.preventDefault();
const email = this.email.value;
const password = this.password.value;
if(password.length > 6){
this.state.errors.password= "Password must be at least 6 characters";
}
const creds = {email, password}
if(creds){
this.props.signUp(creds);
this.props.history.push('/');
}
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<h1>Sign Up</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input
name="email"
type="email"
className="form-control"
id="email"
ref={(input) => this.email = input}
aria-describedby="emailHelp"
placeholder="Enter email" />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label htmlFor="exampleInputPassword1">Password</label>
{this.password > 6 &&
//display an error here
<h2>{this.state.errors.password}</h2>
}
<input
name="password"
type="password"
ref={(input) => this.password = input}
value={this.state.password}
className="form-control"
id="password"
placeholder="Password" />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
I'm quite unsure how to do a form validation within react.
I want to display an error message on the current page saying. However, the password validation is ignored so their is no error that shows on the page.
Password must be at least characters
Maybe i'm not using conditional rendering right
SignUp.js (snippet for demonstration purpose)
constructor(props){
super(props);
this.state ={
errors: {},
}
handleSubmit(event) {
event.preventDefault();
const email = this.email.value;
const password = this.password.value;
if(password.length > 6){
this.state.errors.password= "Password must be at least 6 characters";
}
const creds = {email, password}
if(creds){
this.props.signUp(creds);
this.props.history.push('/');
}
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<h1>Sign Up</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input
name="email"
type="email"
className="form-control"
id="email"
ref={(input) => this.email = input}
aria-describedby="emailHelp"
placeholder="Enter email" />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label htmlFor="exampleInputPassword1">Password</label>
{this.password > 6 &&
//display an error here
<h2>{this.state.errors.password}</h2>
}
<input
name="password"
type="password"
ref={(input) => this.password = input}
value={this.state.password}
className="form-control"
id="password"
placeholder="Password" />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
Share
Improve this question
asked Jan 4, 2019 at 2:52
Eric ThomasEric Thomas
211 gold badge2 silver badges6 bronze badges
2
-
It looks like you need to change
this.password > 6
tothis.state.password.length < 6
– ic3b3rg Commented Jan 4, 2019 at 2:55 -
im getting
TypeError: Cannot read property 'length' of undefined
– Eric Thomas Commented Jan 4, 2019 at 2:58
4 Answers
Reset to default 2You should store the form data in your ponent's state. For example, have this.state.email
instead of this.email
. When data that is stored in a ponent's state is updated, a rerender is triggered. However, rerenders are not triggered for updates to plain class variables. You also directly manipulate the state when you set the errors
. Instead you should use the setState
method docs.
The reason you do not see your error shown on the page is because your page is not properly rerendering due to changes in the form.
Note it is also a good idea to wrap your form data variables in a formData
object within state for organization. This helps keep the form's data separate from the rest of the ponent's state (e.g. keeps form values separate from your errors variable) and allows the form data to be passed around more easily, for example during form submission.
Here is an example of how you can reorganize things:
constructor(props){
super(props);
this.state = {
formData: { // set up default form values
email: "",
password: "",
},
errors: {},
}
handleChange = event => {
const { formData } = this.state;
this.setState({
formData: {
...formData, // leave other values unchanged
[event.target.name]: event.target.value, // update the changed value
}
});
}
handleSubmit(event) {
event.preventDefault();
const { formData, errors } = this.state;
const { email, password } = formData;
if (password.length < 6) { // changed parison to _less_ than
this.setState({ // update errors using setState -- never directly modify a ponent's state
errors: {
...errors,
password: "Password must be at least 6 characters",
}
});
}
const creds = {email, password}
if (creds.email && creds.password) { // objects are never falsey, so we need to check each field directly
this.props.signUp(creds);
this.props.history.push('/');
}
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<h1>Sign Up</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input
name="email"
type="email"
className="form-control"
id="email"
value={ this.state.formData.email } {/* control ponent by storing value in state and updating state when the input changes */}
onChange={ this.handleChange }
aria-describedby="emailHelp"
placeholder="Enter email" />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label htmlFor="exampleInputPassword1">Password</label>
{this.state.errors.password &&
//display an error here
<h2>{this.state.errors.password}</h2>
}
<input
name="password"
type="password"
value={ this.state.formData.password }
onChange={ this.handleChange }
className="form-control"
id="password"
placeholder="Password" />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
Note: If your form has checkbox inputs, the shown handleChange
method will not work as desired for those fields since the checked
property of a checkbox input should be used instead of the value
property. For simplicity, I've only included the non-checkbox case, but here is a full version of handleChange
:
handleChange = event => {
const { formData } = this.state;
const { name, type, value, checked } = event.target;
this.setState({
formData: {
...formData,
[name]: type === "checkbox" ? checked : value,
}
})
}
This example isn't using the ponent state to manage the Password and Email fields.
By having them as controlled input fields you can then validate them on change and conditionally render as error message (which doesn't need to be held in state)
eg. if you wanted to show an error is the password is too short:
{ this.state.password.length < 5 && <p>Password too short</p> }
Maybe check out https://github./jaredpalmer/formik and look at some examples.
I think your code to show error show use state
{this.state.errors.password &&
//display an error here
<h2>{this.state.errors.password}</h2>
}
And in submit form you should update your state
if(password.length > 6){
this.setState({ errors: { password: "Password must be at least 6 characters"}
})
} else {
this.setState({errors: {}})
}
I have built custom hook for easy form validation. i think it's gonna make your life a lot easier es to form. you can leverage your html validation knowledge.
Github: https://github./bluebill1049/react-hook-form
Website: http://react-hook-form.now.sh
import React from 'react'
import useForm from 'react-hook-form'
function App() {
const { register, handleSubmit, errors } = useForm() // initialise the hook
const onSubmit = (data) => { console.log(data) } // callback when validation pass
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input name="firstname" ref={register} /> {/* register an input */}
<input name="lastname" ref={register({ required: true })} /> {/* apply required validation */}
{errors.lastname && 'Last name is required.'} {/* error message */}
<input name="age" ref={register({ pattern: /\d+/ })} /> {/* apply a Refex validation */}
{errors.age && 'Please enter number for age.'} {/* error message */}
<input type="submit" />
</form>
)
}