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());
}
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 trying to validate my form but always get the same error,
this the code of the form:
import React from "react";
import zxcvbn from "zxcvbn";
import logo from "../images/milinus.png";
import useForm from "./useForm";
function Signup() {
//function for validation of the submited data from the form
const validation = (values) => {
let errors = {};
//name validion
if (!values.name.trim()) {
errors.name = "Name is required";
}
//last name validtion
if (!values.lastname.trim()) {
errors.lastname = "Username is required";
}
//email validtion
if (!values.email.trim()) {
errors.email = "Email is required";
} else if (!/\S+#\S+\.\S+/.test(values.email)) {
errors.email = "Email is invalid";
}
//password validtion
if (!values.pass.trim()) {
errors.pass = "pass is required";
} else if (values.pass < 8) {
errors.pass = "PassWord need to be 8 characters or more";
}
//pass_conf
if (!values.pass_conf.trim()) {
errors.pass_conf = "pass_conf is required";
} else if (values.pass_conf == !values.pass) {
errors.pass_conf = "pass_conf is not match the Password";
}
return errors;
}
//custom hook for the form
const { hadleChange, values, handleSubmit, errors } = useForm(validation);
//function that conforme and indicaite the score of the pass word
const confirm_ps = (e) => {
const weak = document.querySelector(".weak");
const muduim = document.querySelector(".muduim");
const strong = document.querySelector(".strong");
const indicater = document.querySelector(".indicater");
let test = zxcvbn(e.target.value);
weak.setAttribute("style", "background-color:white");
muduim.setAttribute("style", "background-color:white");
strong.setAttribute("style", "background-color:white");
indicater.innerHTML = "";
console.log(test.score);
if (test.score === 0 || test.score === 1) {
if (e.target.value == !null) {
weak.setAttribute("style", "background-color:white");
muduim.setAttribute("style", "background-color:white");
strong.setAttribute("style", "background-color:white");
indicater.innerHTML = "";
}
console.log(e.target.value);
weak.setAttribute("style", "background-color:red");
indicater.innerHTML = "Weak";
} else if (test.score === 2 || test.score === 3) {
weak.setAttribute("style", "background-color:yellow");
muduim.setAttribute("style", "background-color:yellow");
indicater.innerHTML = "Meduim";
} else if (test.score === 4) {
weak.setAttribute("style", "background-color:green");
muduim.setAttribute("style", "background-color:green");
strong.setAttribute("style", "background-color:green");
indicater.innerHTML = "Strong";
}
};
return (
<div className="signup">
<div className="logo">
<img src={logo} alt="logo" />
<p>CREER UN COMPTE</p>
</div>
<div className="inputs">
<form className="form" onSubmit={handleSubmit}>
<div className="form-input">
<input
type="text"
name="name"
id="name"
placeholder="Nom"
value={values.name}
onChange={hadleChange}
/>
<p className="errorname">{errors.name}</p>
</div>
<div className="form-input ">
<input
type="text"
name="lastname"
id="lastname"
placeholder="Prenom"
value={values.lastname}
onChange={hadleChange}
/>
<p className="errorlastname"></p>
</div>
<div className="form-input">
<input
type="text"
id="username"
name="username"
placeholder="Username"
value={values.username}
onChange={hadleChange}
/>
<p className="errorusername"></p>
</div>
<div className="form-input">
<input
type="text"
id="email"
name="email"
placeholder="Email"
value={values.email}
onChange={hadleChange}
/>
<p className="erroremail"></p>
</div>
<div className="form-input">
<input
type="password"
id="pass"
name="pass"
placeholder="Mote de pass"
onChange={confirm_ps}
/>
<p className="errorpassword"></p>
</div>
<div className="form-input">
<input
type="password"
id="pass_conf"
className="conform"
name="pass_conf"
placeholder="conform le mote de pass"
value={values.pass_conf}
onChange={hadleChange}
/>
<p className="errorpass_conf"></p>
</div>
<div className="progress">
<span className="weak"></span>
<span className="muduim"></span>
<span className="strong"></span>
</div>
<div className="indicater"></div>
<div className="wornings">
<ul>
<li>Letters majuscule et minuscule</li>
<li>Plus de 8 characters</li>
<li>Contiens au moin un chiffers ou symbol</li>
</ul>
</div>
<button type="submite" className="signup-btn">
S'INSCRIRE AND ACCEPTER
</button>
</form>
</div>
</div>
);
}
export default Signup;
and this the code for the custom hook:
import { useState, useEffect } from "react";
const useForm = (callback,validation) => {
const { values, setValues } = useState({
name: "",
lastname: "",
username: "",
email: "",
pass: "",
pass_conf: "",
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
setErrors(validation(values));
setIsSubmitting(true);
};
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
}, [errors]);
return { handleChange, handleSubmit, values, errors };
};
export default useForm;
when i click on the submit button i get this errors:
TypeError: validation is not a function
22 |
23 | const handleSubmit = (e) => {
24 | e.preventDefault();
25 | setErrors(validation(values));
| ^ 26 | setIsSubmitting(true);
27 | };
You are setting two parameters for the hook - a callback function and validation function, and you are only passing the validation function
useForm(validation)
Please pass the callback function first and then that vaildation function
Because you pass only callback, not validation. You need decalre callBack and pass it into useForm
const callBack = () => {...};
useForm(callBack, validation);
Your useForm receives two params where you only give it one in the callback
const useForm = (callback,validation)
As a result, it got error here:
setErrors(validation(values));
I think your logic is somewhat not clear.
const { hadleChange, values, handleSubmit, errors } = useForm(validation);
Whatever, you passed validation as a callback here.
How about changing it as follow.
const useForm = (validation, callback) => {
To use the callback, you can define the callback here.
const { hadleChange, values, handleSubmit, errors } = useForm(validation, function() {... callback here});
I want to add a red border only if an input is empty. I couldn't find a way to "addClass" in React so I'm using state. Right now the code will add red border to all inputs, even if it has text.
State:
this.state = {
inputBorderError: false,
};
HTML/JSX:
<label>Name</label>
<input className={
this.state.inputBorderError ? 'form-input form-input-fail' : 'form-input'
} />
<label>Email</label>
<input className={
this.state.inputBorderError ? 'form-input form-input-fail' : 'form-input'
} />
<label>Message</label>
<textarea className={
this.state.inputBorderError ? 'form-input form-input-fail' : 'form-input'
} />
CSS:
.form-input-fail {
border: 1px solid red;
}
JS:
let inputFields = document.getElementsByClassName('form-input');
for (var i = 0; i < inputFields.length; i++) {
if (inputFields[i].value === '') {
this.setState({
inputBorderError: true,
});
}
}
I see the error in my code as it's basically setting the state to true anytime it finds an empty input. I think I may be approaching this incorrectly as there's only one state. Is there a solution based on my state approach, or is there another solution?
Right now, you have single state-value that affects all inputs, you should consider having one for each input. Also, your inputs are not controlled, it will be harder to record and track their values for error-handling.
It is good practice to give each input tag a name property. Making it easier to dynamically update their corresponding state-value.
Try something like the following, start typing into each input, then remove your text: https://codesandbox.io/s/nervous-feynman-vfmh5
class App extends React.Component {
state = {
inputs: {
name: "",
email: "",
message: ""
},
errors: {
name: false,
email: false,
message: false
}
};
handleOnChange = event => {
this.setState({
inputs: {
...this.state.inputs,
[event.target.name]: event.target.value
},
errors: {
...this.state.errors,
[event.target.name]: false
}
});
};
handleOnBlur = event => {
const { inputs } = this.state;
if (inputs[event.target.name].length === 0) {
this.setState({
errors: {
...this.state.errors,
[event.target.name]: true
}
});
}
};
handleOnSubmit = event => {
event.preventDefault();
const { inputs, errors } = this.state;
//create new errors object
let newErrorsObj = Object.entries(inputs)
.filter(([key, value]) => {
return value.length === 0;
})
.reduce((obj, [key, value]) => {
if (value.length === 0) {
obj[key] = true;
} else {
obj[key] = false;
}
return obj;
}, {});
if (Object.keys(newErrorsObj).length > 0) {
this.setState({
errors: newErrorsObj
});
}
};
render() {
const { inputs, errors } = this.state;
return (
<div>
<form onSubmit={this.handleOnSubmit}>
<label>Name</label>
<input
className={
errors.name ? "form-input form-input-fail" : "form-input"
}
name="name"
value={inputs.name}
onChange={this.handleOnChange}
onBlur={this.handleOnBlur}
/>
<label>Email</label>
<input
className={
errors.email ? "form-input form-input-fail" : "form-input"
}
name="email"
value={inputs.email}
onChange={this.handleOnChange}
onBlur={this.handleOnBlur}
/>
<label>Message</label>
<textarea
className={
errors.message ? "form-input form-input-fail" : "form-input"
}
name="message"
value={inputs.message}
onChange={this.handleOnChange}
onBlur={this.handleOnBlur}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
You are correct that there is only one state.
What you need to do is store a separate error for each input. one way to do this is with a set or array on state like state = {errors: []} and then check
<label>Name</label>
<input className={
this.state.errors.includes('name') ? 'form-input form-input-fail' : 'form-input'
} />
<label>Email</label>
<input className={
this.state.errors.includes('email') ? 'form-input form-input-fail' : 'form-input'
} />
} />
You should keep track of the input value in the state instead of checking for borderStyling state only.
Base on your code, you could refactor it to something like this:
// keep track of your input changes
this.state = {
inputs: {
email: '',
name: '',
comment: '',
},
errors: {
email: false,
name: false,
comment: false,
}
};
// event handler for input changes
handleChange = ({ target: { name, value } }) => {
const inputChanges = {
...state.inputs,
[name]: value
}
const inputErrors = {
...state.errors,
[name]: value == ""
}
setState({
inputs: inputChanges,
errors: inputErrors,
});
}
HTML/JSX
// the name attribut for your input
<label>Name</label>
<input name="name" onChange={handleChange} className={
this.errors.name == "" ? 'form-input form-input-fail' : 'form-input'
} />
<label>Email</label>
<input name="email" onChange={handleChange} className={
this.errors.email == "" ? 'form-input form-input-fail' : 'form-input'
} />
<label>Message</label>
<textarea name="comment" onChange={handleChange} className={
this.errors.comment == "" ? 'form-input form-input-fail' : 'form-input'
} />
And if you are probably looking at implementing it with CSS and js, you can try this article matching-an-empty-input-box-using-css-and-js
But try and learn to make your component reusable and Dry, because that is the beginning of enjoying react app.
[Revised]
ReactJs
I'm developing a simple form using ReactJS and I made a form and validations.
I made the form and handled the validations
But now I need to make it send an email with the form details using SendGrid or something similar
This is my code - App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Form from './Form.js';
class App extends Component {
render() {
return (
<div className="App">
<h2>React Form - Jones Task</h2>
<Form />
</div>
);
}
}
export default App;
Form.Js
import React, { Component } from 'react';
import { FormErrors } from './FormErrors';
import './Form.css';
class Form extends Component {
constructor (props) {
super(props);
this.state = {
fname: '',
lname: '',
email: '',
phone: '',
formErrors: {fname: '', lname: '', email: '', phone: ''},
fnameValid: false,
lnameValid: false,
emailValid: false,
phoneValid: false,
formValid: false
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleUserInput = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value},
() => { this.validateField(name, value) });
}
validateField(fieldName, value) {
let fieldValidationErrors = this.state.formErrors;
let fnameValid = this.state.fnameValid;
let lnameValid = this.state.lnameValid;
let emailValid = this.state.emailValid;
let phoneValid = this.state.phoneValid;
switch(fieldName) {
case 'fname':
fnameValid = value.match(/^[a-zA-Z]*$/);
fieldValidationErrors.first = fnameValid ? '' : ' name is invalid. ';
fieldValidationErrors.first += value.length >= 2 ? '' : ' name should contain at least 2 chars. ';
break;
case 'lname':
lnameValid = value.match(/^[a-zA-Z]*$/) && value.length >= 2;
fieldValidationErrors.last = lnameValid ? '' : ' name is invalid. ';
fieldValidationErrors.last += value.length >= 2 ? '' : ' name should contain at least 2 chars. ';
break;
case 'email':
emailValid = value.match(/^([\w.%+-]+)#([\w-]+\.)+([\w]{2,})$/i);
fieldValidationErrors.email = emailValid ? '' : ' is invalid. ';
break;
case 'phone':
phoneValid = value.length == 10;
fieldValidationErrors.phone = phoneValid ? '' : ' should contain 10 digits exactly. ';
fieldValidationErrors.phone += value.match(/^\d+$/) ? '' : ' should contain numbers only. ';
break;
default:
break;
}
this.setState({formErrors: fieldValidationErrors,
fnameValid: fnameValid,
lnameValid: lnameValid,
emailValid: emailValid,
phoneValid: phoneValid
}, this.validateForm);
}
validateForm() {
this.setState({formValid: this.state.fnameValid && this.state.lnameValid && this.state.emailValid && this.state.phoneValid});
}
errorClass(error) {
return(error.length === 0 ? '' : 'has-error');
}
handleSubmit(event) {
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'avivday#gmail.com',
from: 'avivday#gmail.com',
subject: 'JonesForm',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg);
event.preventDefault();
}
render () {
return (
<form className="demoForm" onSubmit={this.handleSubmit}>
<div className="panel panel-default">
<FormErrors formErrors={this.state.formErrors} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.fname)}"}>
<input type="text" className="form-control" name="fname"
placeholder="First Name"
value={this.state.fname}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.lname)}"}>
<input type="text" className="form-control" name="lname"
placeholder="Last Name"
value={this.state.lname}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.email)}"}>
<input type="email" required className="form-control" name="email"
placeholder="Email"
value={this.state.email}
onChange={this.handleUserInput} />
</div>
<br />
<div className={"form-group ${this.errorClass(this.state.formErrors.phone)}"}>
<input type="text" className="form-control" name="phone"
placeholder="Phone Number"
value={this.state.phone}
onChange={this.handleUserInput} />
</div>
<br/>
<button type="submit" disabled={!this.state.formValid}>Submit</button>
</form>
)
}
}
export default Form;
On submit, nothing happens.
I did echo the enviorment on node.js command prompt
Thank you!
I have a form in react that I'm refactoring. I'm going to move most of the state and logic to the parent, because the parent state will be updated with the form result... but I was going to refactor before and can't seem to get a switch statement to work. I was told it would help performance in the long run.
The Validate function is where I'm trying to add a switch statement.
import React from 'react'
import styles from './style.addLibForm.css'
class AddLibForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: {
title: "",
content: "",
imgURL: ""
},
blurred: {
title: false,
content: false,
imgURL: ""
},
formIsDisplayed: this.props.toggle
};
}
handleInputChange(newPartialInput) {
this.setState(state => ({
...state,
input: {
...state.input,
...newPartialInput
}
}))
}
handleBlur(fieldName) {
this.setState(state => ({
...state,
blurred: {
...state.blurred,
[fieldName]: true
}
}))
}
***//TURN INTO SWITCH STATEMENT!***
validate() {
const errors = {};
const {input} = this.state;
if (!input.title) {
errors.title = 'Title is required';
} //validate email
if (!input.content) {
errors.content = 'Content is required';
}
if (!input.imgURL) {
errors.imgURL = 'Image URL is required';
}
console.log(Object.keys(errors).length === 0);
return {
errors,
isValid: Object.keys(errors).length === 0
};
}
render() {
const {input, blurred} = this.state;
const {errors, isValid} = this.validate();
return (
<div className="flex">
<form
className={styles.column}
onSubmit={(e) =>
{ e.preventDefault();
this.setState({})
return console.log(this.state.input);
}}>
<h2> Add a library! </h2>
<label>
Name:
<input
className={styles.right}
name="title"
type="text"
value={input.title}
onBlur={() => this.handleBlur('title')}
onChange={e => this.handleInputChange({title: e.target.value})}/>
</label>
<br/>
<label>
Content:
<input
className={styles.right}
name="content"
type="text"
value={input.content}
onBlur={() => this.handleBlur('content')}
onChange={e => this.handleInputChange({content: e.target.value})}/>
</label>
<br/>
<label>
Image URL:
<input
className={styles.right}
name="imgURL"
type="text"
value={input.imgURL}
onBlur={() => this.handleBlur('imgURL')}
onChange={e => this.handleInputChange({imgURL: e.target.value})}/>
</label>
<br/>
<p>
<input className={styles.button} type="submit" value="Submit" disabled={!isValid}/>
</p>
{/* CSS THESE TO BE INLINE WITH INPUTS */}
<br/>
{blurred.content && !!errors.content && <span>{errors.content}</span>}
<br/>
{blurred.title && !!errors.title && <span>{errors.title}</span>}
<br/>
{blurred.imgURL && !!errors.imgURL && <span>{errors.imgURL}</span>}
</form>
</div>
);
}
}
export default AddLibForm
I've was putting the switch statement inside the validate function. I tried inputs, errors, this.state.input, this.state.errors, {input}... what am I missing?
Switch statement makes most sense when you need to compare one variable with different values
if(x === 1){
//...
} else if(x === 2) {
//...
} else if(x === 3) {
//...
} else {} {
//...
}```
=>
switch (x) {
case 1:
//...
break;
case 2:
//...
break;
case 3:
//...
break;
default:
//...
}
since it clearly expresses this "1 variable to many values" mapping condition.
In your case though you don't compare one var to many values, you simply check multiple variables for existence, i.e. semantically compare them with a truthy value, so switch will not make much sense.
It's better to leave as is since all these ifs are checking different conditions and are still pretty readable.
Overall you can check this article for a more verbose directions on approaching condition handling more efficiently.