Cant log in with the first time, OnSubmit - reactjs

I am validating some details, the problem is when the validation is complete and I want to move to the next page with submit. it logs in with the second try
I have tried to put all the validation process to the OnChange function, but it messes up all of the validation process, I have also tried to put the error variables to the state but I receive an error message that It's constant variables and can't be changed.
import { Link } from 'react-router-dom';
class Profile extends Component {
state = {
details: {
firstName: '',
lastName: '',
id: '',
email: ''
},
error: false,
complete: false
};
OnSubmit = e => {
e.preventDefault();
let 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,}))$/;
const { email } = this.state.details;
const { firstName } = this.state.details;
const { lastName } = this.state.details;
const { id } = this.state.details;
let errorid = false;
let errorfirstlast = false;
let erroremail = false;
if (id.length <= 9 && id !== '') {
console.log('trueid');
errorid = false;
} else {
errorid = true;
console.log('falseid');
}
if (re.test(email)) {
console.log('trueemail');
erroremail = false;
} else {
erroremail = true;
console.log('falseemail');
}
if (
firstName !== '' &&
lastName !== '' &&
firstName.substr(0, 1) === firstName.substr(0, 1).toUpperCase() &&
lastName.substr(0, 1) === lastName.substr(0, 1).toUpperCase() &&
!firstName.match(/\d/) &&
!lastName.match(/\d/)
) {
console.log('truefirstlast');
errorfirstlast = false;
} else {
errorfirstlast = true;
console.log('falsefirstlast');
}
if (erroremail === true || errorfirstlast === true || errorid === true) {
this.setState({ error: true });
} else {
this.setState({ error: false });
this.setState({ complete: true });
}
};
OnChange = e => {
e.preventDefault();
this.setState({
details: { ...this.state.details, [e.target.name]: e.target.value }
});
};
render() {
return (
<div>
<div className="container text-center mt-4" style={{ width: '500px' }}>
<form className="px-4 py-3" onSubmit={this.OnSubmit}>
<div className="form-group">
{this.state.error === true ? (
<p className="text-danger">
Some of the details are wrong check the fields above
</p>
) : null}
<label>First Name:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="firstName"
/>
</div>
<div className="form-group">
<label>Last Name:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="lastName"
/>
</div>
<div className="form-group">
<label>ID Number:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="id"
/>
</div>
<div className="form-group">
<label>Email:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="email"
/>
</div>
{this.state.complete === true ? (
<Link to="/success">
<button type="submit" className="btn btn-secondary mt-3">
Check
</button>
</Link>
) : (
<button type="submit" className="btn btn-secondary mt-3">
Check
</button>
)}
</form>
</div>
</div>
);
}
}
export default Profile;
The problem is that I enter the next page with the second click on the submit button, I want to enter with the first try

The reason that routing to the next page only happens on the second click of the Submit button is because the Link to the next page is only rendered after this.state.complete is true, which only happens after hitting submit for the first time.
In other words, the flow you have currently is:
Fill in form, when done, hit the "Check" button
Hitting the "Check" button triggers the onSubmit function. If the input data is valid, set the state variable complete to true.
Updating state triggers a re-render and now the "Check" button contains the Link to the next page.
Click the "Check" button again, which will click the Link and take the user to the next page.
(Note: perhaps the flow would be more clear if you separated the Link from the submit button)
If you want the user to be taken to the next page on the first click of the "Check" button (if the input data is valid), you can control the routing programmatically inside of the onSubmit function, rather than relying on a Link to be clicked. This can be done by passing the desired route to history.push. In fact, with this method, we can do away with the complete state variable entirely (and the Link in your render method).
For example:
import { withRouter } from "react-router-dom";
class Profile extends Component {
onSubmit = e => {
// ... validation
if (erroremail || errorfirstlast || errorid) {
this.setState({ error: true });
} else {
this.props.history.push("/success");
}
}
}
export default withRouter(Profile);
Note that you'll need to wrap Profile in withRouter in order to access this.props.history.
Also a small code-style note: checking if a boolean is equal to true is redundant, since the value itself is already either true or false (which are the outputs of the === operator anyway).

setState is asynchronous, wrapping it in a function call will make it work for you.
if (erroremail === true || errorfirstlast === true || errorid === true) {
this.setState(() => { error: true });
} else {
this.setState(() => { error: false, complete: true });
}
}

I solved this by moving all of the validation to OnChange function.
e.preventDefault();
let 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,}))$/;
const details = { ...this.state.details, [e.target.name]: e.target.value };
this.setState({
details,
complete:
details.id.length <= 9 &&
details.id !== '' &&
re.test(details.email) &&
details.firstName !== '' &&
details.lastName !== '' &&
details.firstName.substr(0, 1) ===
details.firstName.substr(0, 1).toUpperCase() &&
details.lastName.substr(0, 1) ===
details.lastName.substr(0, 1).toUpperCase() &&
!details.firstName.match(/\d/) &&
!details.lastName.match(/\d/)
});
and only set the error in submit .

Related

How to transform Class based component to Functional component with multiple state values?

The reason and an introduction.
Based on an exercise, I want to build a form for people to book a session for a general subject, like appointment for doctor etc. I must say that actually I am very new to ReactJS, and how you can work and build apps. More specifically, I am trying to understand the concept-pattern of how React works if you want to write something on a form, click the submit button and print it in the same page in a list. That means I must understand the state procedure and the functions that you have to write in order to accomplish the
write the data in an input form
submit it
print it somewhere in the same page.
The process so far…
Based on some research, I found a very similar code that you can accomplish more than 50% of what I need to do. After some days, I finally changed the existing code to make it more appropriate for the instructions of the exercise. That means it's around 90% of what I must solve for the exercise.
The problem
The original code was made with class based components, and I want to refactor it and make it work with functional components. I am very confused on how I must interact with each function and the state in order to make it work. In other words, I can't understand the pattern of the state and how we can manipulate it.
More specifically in details
The original code:
import React from 'react';
const validEmailRegex = RegExp(
/^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i
);
const validateForm = errors => {
let valid = true;
Object.values(errors).forEach(val => val.length > 0 && (valid = false));
return valid;
};
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fullName: null,
email: null,
password: null,
errors: {
fullName: '',
email: '',
password: '',
}
};
}
handleChange = (event) => {
event.preventDefault();
const { name, value } = event.target;
let errors = this.state.errors;
switch (name) {
case 'fullName':
errors.fullName =
value.length < 5
? 'Full Name must be at least 5 characters long!'
: '';
break;
case 'email':
errors.email =
validEmailRegex.test(value)
? ''
: 'Email is not valid!';
break;
case 'password':
errors.password =
value.length < 8
? 'Password must be at least 8 characters long!'
: '';
break;
default:
break;
}
this.setState({errors, [name]: value});
}
handleSubmit = (event) => {
event.preventDefault();
if(validateForm(this.state.errors)) {
console.info('Valid Form')
}else{
console.error('Invalid Form')
}
}
render() {
const {errors} = this.state;
return (
<div className='wrapper'>
<div className='form-wrapper'>
<h2>Create Account</h2>
<form onSubmit={this.handleSubmit} noValidate>
<div className='fullName'>
<label htmlFor="fullName">Full Name</label>
<input type='text' name='fullName' onChange={this.handleChange} noValidate />
{errors.fullName.length > 0 &&
<span className='error'>{errors.fullName}</span>}
</div>
<div className='email'>
<label htmlFor="email">Email</label>
<input type='email' name='email' onChange={this.handleChange} noValidate />
{errors.email.length > 0 &&
<span className='error'>{errors.email}</span>}
</div>
<div className='password'>
<label htmlFor="password">Password</label>
<input type='password' name='password' onChange={this.handleChange} noValidate />
{errors.password.length > 0 &&
<span className='error'>{errors.password}</span>}
</div>
<div className='submit'>
<button>Create</button>
</div>
</form>
</div>
</div>
);
}
}
What i have done so far, but still with class based component:
import React from 'react';
import './App.css';
const validEmailRegex = RegExp(
/^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i
);
// or /^[a-z ,.'-]+$/i
const validNameRegex = RegExp(/^[a-z ,.'-]+$/i);
const validateForm = errors => {
let valid = true;
Object.values(errors).forEach(val => val.length > 0 && (valid = false));
return valid;
};
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: null,
lastName: null,
email: null,
people: null,
dateTime: null,
errors: {
firstName: '',
lastName: '',
email: '',
people: '',
dateTime: '',
}
};
}
handleChange = (event) => {
event.preventDefault();
const { name, value } = event.target;
let errors = this.state.errors;
switch (name) {
case 'firstName':
errors.firstName = value.length > 3 && validNameRegex.test(value)
? ''
: 'First name is not valid! Must be at least 3 letters long!';
break;
case 'lastName':
errors.lastName = value.length > 3 && validNameRegex.test(value)
? ''
: 'Last name is not valid! Must be at least 3 letters long!';
break;
case 'email':
errors.email =
validEmailRegex.test(value)
? ''
: 'Email is not valid!';
break;
case 'people':
errors.people =
value < 1
? 'Type least 1 person!'
: '';
case 'dateTime':
errors.dateTime =
value.min < 'T07:00' && value.max > 'T7:00'
? 'Time must be at least 07:00!'
: '';
break;
default:
break;
}
this.setState({ errors, [name]: value });
}
handleSubmit = (event) => {
event.preventDefault();
if (validateForm(this.state.errors)) {
console.info('Valid Form')
} else {
console.error('Invalid Form')
}
}
render() {
const { errors } = this.state;
return (
<div className='App'>
<div className='form'>
<h2>Appointments</h2>
<form onSubmit={this.handleSubmit}>
{/* First Name */}
<div className='firstLastName'>
<label>First Name</label>
<input type='text' name='firstName' onChange={this.handleChange} required/>
{errors.firstName.length > 0 &&
<span className='error'>{errors.firstName}</span>}
</div>
{/* Last Name */}
<div className='firstLastName'>
<label>Last Name</label>
<input type='text' name='lastName' onChange={this.handleChange} required />
{errors.lastName.length > 0 &&
<span className='error'>{errors.lastName}</span>}
</div>
{/* Email */}
<div className='email'>
<label>Email</label>
<input type='email' name='email' onChange={this.handleChange} required />
{errors.email.length > 0 &&
<span className='error'>{errors.email}</span>}
</div>
{/* people */}
<div className='people'>
<label>Players</label>
<input type="number" min="0" max="25" name="people" onChange={this.handleChange} required/>
{errors.people.length > 0 &&
<span className='error'>{errors.people}</span>}
</div>
{/* Date and Time */}
<div className='people'>
<label>Choose a time for your appointment:</label>
<input type="datetime-local" name="dateTime" onChange={this.handleChange}
min="2022-01-01T07:00" max="2022-12-01T19:00"
pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}"
step="3600"
required />
{errors.dateTime.length < 'T07:00' && errors.dateTime.length > 'T7:00' >
<span className='error'>{errors.dateTime}</span>}
</div>
<div className='submit'>
<button>Submit</button>
</div>
</form>
</div>
</div>
);
}
}
// export default App;
My steps on the transformation to a functional component
import React, { useState } from 'react';
import './App.css';
const validEmailRegex = RegExp(
/^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i
);
// or /^[a-z ,.'-]+$/i
const validNameRegex = RegExp(/^[a-z ,.'-]+$/i);
const validateForm = errors => {
let valid = true;
Object.values(errors).forEach(val => val.length > 0 && (valid = false));
return valid;
};
function App() {
const initialValues = {
firstName: null,
lastName: '',
email: '',
people: '',
dateTime: '',
errors: {
firstName: '',
lastName: '',
email: '',
people: '',
dateTime: '',
}
};
const [values, setValues] = useState(initialValues);
const handleChange = e => {
e.preventDefault();
setValues({ ...values, [e.target.name]: e.target.value })
let errors = values.errors;
switch (values) {
case 'firstName':
errors.firstName = values.name.length > 3 && validNameRegex.test(values)
? ''
: 'First name is not valid! Must be at least 3 letters long!';
break;
default:
break;
}
setValues({ errors, [values]: values });
}
const handleSubmit = e => {
e.preventDefault();
if (validateForm(setValues.errors)) {
console.info('Valid Form')
} else {
console.error('Invalid Form')
}
}
const {errors} = values;
return (
<div className='wrapper'>
<div className='form-wrapper'>
<h2>Appointmnts</h2>
<form onSubmit={handleSubmit}>
{/* First Name */}
<div className='name'>
<label>First Name</label>
<input type='text' name='firstName' onChange={handleChange} />
{errors.firstName.length > 0 &&
<span className='error'>{errors.firstName}</span>}
</div>
<div className='submit'>
<button>Submit</button>
</div>
</form>
</div>
</div>
);
}
export default App;
I tried to break the procedure into steps. First step is to work with only one data and that is the firstName. If the first name is working i will follow the same steps to develop also the other information such as lastName, email.. etc
Lets think it for a while
In the class based code with my implementations, not the original, we have the parts of:
import React from 'react';
import './App.css';
{/* Part 1 : The regex validation for email and names */}
const validEmailRegex = RegExp(
/^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i
);
// or /^[a-z ,.'-]+$/i
const validNameRegex = RegExp(/^[a-z ,.'-]+$/i);
{/* Part 2 : The function to work with the errors */}
const validateForm = errors => {
let valid = true;
Object.values(errors).forEach(val => val.length > 0 && (valid = false));
return valid;
};
{/* Part 3 : The main class component */}
export default class App extends React.Component {
constructor(props) {
super(props);
{/* Part 3.1 : The state values and the error values */}
this.state = {
firstName: null,
lastName: null,
email: null,
people: null,
dateTime: null,
{/* Part 3.1.1 : The error values */}
errors: {
firstName: '',
lastName: '',
email: '',
people: '',
dateTime: '',
}
};
}
{/* Part 3.2 : The function that runs if we have any change inside our inputs */}
handleChange = (event) => {
event.preventDefault();
{/* Question 1: Here we pass each new value from the input as a name and value in the original state? */}
const { name, value } = event.target;
{/* Question 2: Here we pass the errors values in a new errors variable but why ? */}
let errors = this.state.errors;
{/* Part 3.2.1 : The switch case part. Question 3: what data we pass using the `name` variable inside the `switch` ? */}
switch (name) {
case 'firstName':
errors.firstName = value.length > 3 && validNameRegex.test(value)
? ''
: 'First name is not valid! Must be at least 3 letters long!';
break;
case 'lastName':
errors.lastName = value.length > 3 && validNameRegex.test(value)
? ''
: 'Last name is not valid! Must be at least 3 letters long!';
break;
case 'email':
errors.email =
validEmailRegex.test(value)
? ''
: 'Email is not valid!';
break;
case 'people':
errors.people =
value < 1
? 'Type least 1 person!'
: '';
case 'dateTime':
errors.dateTime =
value.min < 'T07:00' && value.max > 'T7:00'
? 'Time must be at least 07:00!'
: '';
break;
default:
break;
}
{/* Question 4: What are we trying to accomplish here? */}
this.setState({ errors, [name]: value });
}
{/* Part 3.3 : When we click the submit button we use the Part2 to validate and print if it is valid or not in the console */}
handleSubmit = (event) => {
event.preventDefault();
if (validateForm(this.state.errors)) {
console.info('Valid Form')
} else {
console.error('Invalid Form')
}
}
{/* Part 4 : The `render` method */}
render() {
{/* Part 4.1 : What do we accomplish here? what state goes inside the errors? Do we get a state from the above code and passing it down or the opposite? Those patterns are some of the React's behaviour that i cant understand. */}
const { errors } = this.state;
{/* Part 4.2 : Here is the main UI that we use the above functions */}
return (
<div className='App'>
<div className='form'>
<h2>Appointments</h2>
<form onSubmit={this.handleSubmit}>
{/* First Name */}
<div className='firstLastName'>
<label>First Name</label>
<input type='text' name='firstName' onChange={this.handleChange} required/>
{/* Question: What exactly are the following `errors` that goes inside the `firstName` and look the lenght of it? */}
{errors.firstName.length > 0 &&
<span className='error'>{errors.firstName}</span>}
</div>
{/* Last Name */}
<div className='firstLastName'>
<label>Last Name</label>
<input type='text' name='lastName' onChange={this.handleChange} required />
{errors.lastName.length > 0 &&
<span className='error'>{errors.lastName}</span>}
</div>
{/* Email */}
<div className='email'>
<label>Email</label>
<input type='email' name='email' onChange={this.handleChange} required />
{errors.email.length > 0 &&
<span className='error'>{errors.email}</span>}
</div>
{/* people */}
<div className='people'>
<label>Players</label>
<input type="number" min="0" max="25" name="people" onChange={this.handleChange} required/>
{errors.people.length > 0 &&
<span className='error'>{errors.people}</span>}
</div>
{/* Date and Time */}
<div className='people'>
<label>Choose a time for your appointment:</label>
<input type="datetime-local" name="dateTime" onChange={this.handleChange}
min="2022-01-01T07:00" max="2022-12-01T19:00"
pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}"
step="3600"
required />
{errors.dateTime.length < 'T07:00' && errors.dateTime.length > 'T7:00' >
<span className='error'>{errors.dateTime}</span>}
</div>
<div className='submit'>
<button>Submit</button>
</div>
</form>
</div>
</div>
);
}
}
// export default App;
Well i know that i wrote a lot for a question but my head is like a spider's web now, i cant understand what are the steps behind the implemantation for this kind of application.
Finally
The end goal here is me to convert the class based component into a functional, but if i can't understand how the original component made and what are the connections between the functions how i will make the transition?
A big thank you for any help.
Have a nice day .

React form error: Form submission canceled because the form is not connected

I have a form inside a class component. When I submit the form, I get a warning Form submission cancelled because the form is not connected. There are no errors—just a warning.
I have linked it to my netlify site. I have added all the form data properties that are required to send the data to netlify.
I have used styled-components for styling.
Not sure, what I am missing.
class ContactThreeForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
phone: "",
message: "",
error: false,
};
}
formSubmit() {
if (
this.state.name === "" ||
this.state.email === "" ||
this.state.phone === "" ||
this.state.message === ""
) {
this.setState({ error: true });
} else {
this.setState({ error: false });
}
this.forceUpdate();
}
check(val) {
if (this.state.error && val === "") {
return false;
} else {
return true;
}
}
render() {
return (
<ContactForm>
<Heading>Get In Touch</Heading>
<Separator />
<form
name="contact"
method="POST"
netlify-honeypot="bot-field"
data-netlify="true"
data-netlify-recaptcha="true"
>
<InputElement>
<Input
type="text"
defaultValue={this.state.name}
className={`name ${this.check(this.state.name) ? "" : "error"}`}
placeholder="Name"
onChange={(e) => this.setState({ name: e.target.value })}
/>
</InputElement>
<InputElement>
<Input
type="text"
defaultValue={this.state.email}
className={`email ${this.check(this.state.email) ? "" : "error"}`}
placeholder="Email"
onChange={(e) => this.setState({ email: e.target.value })}
/>
</InputElement>
<InputElement>
<Input
type="text"
defaultValue={this.state.phone}
className={`phone ${this.check(this.state.phone) ? "" : "error"}`}
placeholder="Phone"
onChange={(e) => this.setState({ phone: e.target.value })}
/>
</InputElement>
<InputElement>
<Textarea
placeholder="Message"
defaultValue={this.state.message}
className={`message ${
this.check(this.state.message) ? "" : "error"
}`}
onChange={(e) => this.setState({ message: e.target.value })}
/>
</InputElement>
<Submit onClick={() => this.formSubmit()}>
<span>Submit</span>
</Submit>
</form>
</ContactForm>
);
}
}
The browser's default handling of a form submission is to make an HTTP POST request to the server and reload the page with whatever is in the response (which may or may not even be content the browser can render). This is almost never what you want in a single page application, because your entire app and all of its state is simply discarded.
Instead of wiring up the form submit as a click handler on the submit button, you should instead wire it up as the onSubmit event of the form. You also want to call preventDefault() on the event, which prevents the native browser behavior. The button doesn't need a click handler at all as long as it has the attribute type="submit" (presumably, inside of your Submit component, there is a regular button element).
formSubmit(event) {
event.preventDefault();
// your submit logic
}
render() {
// ...
<form onSubmit={(event) => this.formSubmit(event)}
// ... Inside `Submit` should be something like:
<button type="submit" />
</form>
}

ReactJS: Handling form validations

I have created the following UI:
Initially I want Movie Name, Ratings and Durations column to have a black border while if a user enters an invalid input (or empty), it should be red. As you can see, Ratings is still red despite the fact that the page has just refreshed. It should be black like its siblings. Can anyone point out why is this happening? Perhaps there is a different way to handle validations in React because of state hooks?
Initial State:
const [validations, setValidations] = useState({
isNameValid: true,
isRatingValid: true,
isDurationValid: true,
});
Submit form if it's valid:
const handleFormSubmit = (event) => {
event.preventDefault();
if (validateInput(newMovie)) {
props.onSubmit(newMovie);
setNewMovie(emptyObject);
}
};
CSS Rule(s):
.invalid {
border-color: rgb(238, 90, 90);
}
validateInput function:
const validateInput = (movieObject) => {
if (movieObject.name.length === 0) {
setValidations((prevState) => {
return { ...prevState, isNameValid: false };
});
} else {
setValidations((prevState) => {
return { ...prevState, isNameValid: true };
});
}
if (movieObject.ratings.length === 0) {
setValidations((prevState) => {
return { ...prevState, isRatingValid: false };
});
} else if (
parseInt(movieObject.ratings) >= 0 &&
parseInt(movieObject.ratings) <= 100
) {
setValidations((prevState) => {
return { ...prevState, isRatingValid: true };
});
}
if (movieObject.duration.length === 0) {
setValidations((prevState) => {
return { ...prevState, isDurationValid: false };
});
} else {
setValidations((prevState) => {
return { ...prevState, isDurationValid: true };
});
}
console.log(validations);
return (
validations.isNameValid &&
validations.isRatingValid &&
validations.isDurationValid
);
};
Based on validations state, className is applied (or not):
<form onSubmit={handleFormSubmit}>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
Movie Name
</label>
<input
className={`${!validations.isNameValid ? "invalid" : ""}`}
type="text"
id="name"
placeholder="Enter Movie Name"
data-testid="nameInput"
value={newMovie.name}
onChange={handleChangeEvent}
/>
</div>
<div className="layout-column mb-15">
<label htmlFor="ratings" className="mb-3">
Ratings
</label>
<input
className={!validations.isRatingsValid ? "invalid" : ""}
type="number"
id="ratings"
placeholder="Enter Rating on a scale of 1 to 100"
data-testid="ratingsInput"
value={newMovie.ratings}
onChange={handleChangeEvent}
/>
</div>
<div className="layout-column mb-30">
<label htmlFor="duration" className="mb-3">
Duration
</label>
<input
className={!validations.isDurationValid ? "invalid" : ""}
type="text"
id="duration"
placeholder="Enter duration in hours or minutes"
data-testid="durationInput"
value={newMovie.duration}
onChange={handleChangeEvent}
/>
</div>
<div className="layout-row justify-content-end">
<button type="submit" className="mx-0" data-testid="addButton">
Add Movie
</button>
</div>
</form>
Anybody can point out a better way to apply validations? I feel like there is a lot of repetition here and the fact that Ratings has a red border even at the start, how can this be fixed?

Submit works with second try

I am validating some details, the problem is when the validation is complete and I want to move to the next page with submit, the first time it's receiving the data, and the second time it's checking if the data is true and moves me to the next page.
I have tried to put all the validation process to the OnChange function, but it messes up all of the validation process, I have also tried to put the error variables to the state but I receive an error message that It's constant variables and can't be changed.
import { Link } from 'react-router-dom';
class Profile extends Component {
state = {
details: {
firstName: '',
lastName: '',
id: '',
email: ''
},
error: false,
complete: false
};
OnSubmit = e => {
e.preventDefault();
let 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,}))$/;
const { email } = this.state.details;
const { firstName } = this.state.details;
const { lastName } = this.state.details;
const { id } = this.state.details;
let errorid = false;
let errorfirstlast = false;
let erroremail = false;
if (id.length <= 9 && id !== '') {
console.log('trueid');
errorid = false;
} else {
errorid = true;
console.log('falseid');
}
if (re.test(email)) {
console.log('trueemail');
erroremail = false;
} else {
erroremail = true;
console.log('falseemail');
}
if (
firstName !== '' &&
lastName !== '' &&
firstName.substr(0, 1) === firstName.substr(0, 1).toUpperCase() &&
lastName.substr(0, 1) === lastName.substr(0, 1).toUpperCase() &&
!firstName.match(/\d/) &&
!lastName.match(/\d/)
) {
console.log('truefirstlast');
errorfirstlast = false;
} else {
errorfirstlast = true;
console.log('falsefirstlast');
}
if (erroremail === true || errorfirstlast === true || errorid === true) {
this.setState({ error: true });
} else {
this.setState({ error: false });
this.setState({ complete: true });
}
};
OnChange = e => {
e.preventDefault();
this.setState({
details: { ...this.state.details, [e.target.name]: e.target.value }
});
};
render() {
return (
<div>
<div className="container text-center mt-4" style={{ width: '500px' }}>
<form className="px-4 py-3" onSubmit={this.OnSubmit}>
<div className="form-group">
{this.state.error === true ? (
<p className="text-danger">
Some of the details are wrong check the fields above
</p>
) : null}
<label>First Name:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="firstName"
/>
</div>
<div className="form-group">
<label>Last Name:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="lastName"
/>
</div>
<div className="form-group">
<label>ID Number:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="id"
/>
</div>
<div className="form-group">
<label>Email:</label>
<input
type="text"
className="form-control"
onChange={this.OnChange}
name="email"
/>
</div>
{this.state.complete === true ? (
<Link to="/success">
<button type="submit" className="btn btn-secondary mt-3">
Check
</button>
</Link>
) : (
<button type="submit" className="btn btn-secondary mt-3">
Check
</button>
)}
</form>
</div>
</div>
);
}
}
export default Profile;
The problem is that I enter the next page with the second click on the submit button, I want to enter with the first try
The reason is that this.setState(..) is an async function Reference here.
Moreover you're calling multiple state-operations behind each other instead of putting it in one call like:
if (erroremail === true || errorfirstlast === true || errorid === true) {
this.setState({ error: true });
} else {
this.setState({
error: false,
complete: true
});
}
If you want to to an action after this state operation is done you can add a callback to the operation:
if (erroremail === true || errorfirstlast === true || errorid === true) {
this.setState({ error: true });
} else {
this.setState({
error: false,
complete: true
}, this.submitMyStuff);
}
const submitMyStuff = () => {
// submit all my stuff
}

Logging in in the second time

I am building a log in, just practice, so the problem is when I click Log in with the correct info, the first time it dont do nothing, it logs in the second time only. I think the problem is that the data stores in the state for first and then It verifies and continue to the next page
.
When the info is wrong it gives the error message for the first time but when I log in it logs in only with the second try
import { Link } from 'react-router-dom';
class Index extends Component {
state = {
errorMessage: false,
isValueCorrect: false,
info: {
email: '',
password: ''
},
login: {
email: 'Email#gmail.com',
password: '1234'
}
};
updateInfo = e => {
this.setState({
info: { ...this.state.login, [e.target.name]: e.target.value }
});
};
submit = e => {
e.preventDefault();
if (
this.state.info.email === this.state.login.email &&
this.state.info.password === this.state.login.password
) {
this.setState({ isValueCorrect: true });
} else {
this.setState({ errorMessage: true });
}
};
render() {
return (
<div className="text-center container mt-4" style={{ width: '50%' }}>
<form className="px-4 py-3" onSubmit={this.submit}>
<div className="form-group">
{this.state.errorMessage === true ? (
<p className="text-danger">The email or the password is wrong</p>
) : null}
<label>Email: </label>
<input
type="text"
placeholder="Email#example.com"
className="form-control"
name="email"
value={this.state.info.email}
onChange={this.updateInfo}
/>
</div>
<div className="form-group">
<label>Password: </label>
<input
type="text"
placeholder="Password"
className="form-control"
name="password"
value={this.state.info.password}
onChange={this.updateInfo}
/>
</div>
{this.state.isValueCorrect === true ? (
<Link to="Profile">
<button type="submit" className="btn btn-secondary mt-3">
Log in
</button>
</Link>
) : (
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
)}
<div>
<Link to="/register" className="badge badge-light p-2 m-2">
Register
</Link>
</div>
</form>
</div>
);
}
}
export default Index;
You're indeed correct in your assessment that the first click only validates the data. You're displaying either a submit button or a link pretending to be a submit button based on the isValueCorrect state flag. That flag is only set once the form is submitted whereas it should be set when the values are correct. There are a few ways you could fix this. Choose one, not both.
1. Set the flag when the values are correct.
updateInfo = e => {
const info = { ...this.state.info, [e.target.name]: e.target.value };
this.setState({
info,
isValueCorrect: (
info.email === this.state.login.email &&
info.password === this.state.login.password
)
});
};
And only handle error in submit.
submit = e => {
e.preventDefault();
if (!this.state.isValueCorrect) {
this.setState({ errorMessage: true });
}
};
2. Use Redirect instead of Link
import { Redirect } from 'react-router-dom';
Replace the conditional {this.state.isValueCorrect === true ... ternary with
<button type="submit" className="btn btn-secondary mt-3">
Sign in
</button>
and add a conditional path to render.
render() {
if (isValuesCorrect) return <Redirect to="Profile" />
return (
...
);
}
Other mistakes
login in state is never changed, so it should be a static constant of the class instead of a filed in state.
updateInfo spreads state.login into state.info, when it should propably spread state.info instead.

Resources