React - Add Class to Empty Input Fields - javascript

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]

Related

Form required section in react

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

Validating variables in the state used in render() to style/populate html

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());
}

Updating React Json Value Conditionally

I didn't figure out how to edit JSON data from a Put request.(it must be PUT request)
I created some inputs as you see and I need to find a way for updating/adding new credit-debit datas on JSON data differs by "to" and "from".
Also, if a "to" value added, it must decreased from total balance and if a "from" value added, it must be added to total balance.
I created a select box and an input for this (didin't connect between json and component)
My Updater component is as follows:
Component itself:
import React, { Component } from 'react';
import './Updater.scss';
import Axios from 'axios';
export default class Updater extends Component {
constructor(){
super();
this.state = {
amount: '',
description: '',
from: '',
to: '',
date: new Date()
}
}
onSubmitEdit = () => {
Axios.put('http://localhost:8080/api/balance/add',
{});
}
render() {
return (
<div className="updatercontainer">
<div className="updaterinputs">
<input className="amount" name="amount"
type="text" placeholder="Enter Amount"/>
<input className="description" name="description"
type="text" placeholder="Enter Description"/>
</div>
<div className="selectbox">
<select>
<option value="From">From</option>
<option value="To">To</option>
</select>
<input className="fromto" type="text"
name="fromto" placeholder="Enter From or To Name"/>
</div>
<div className="selectboxcontainer">
<div className="button-container">
<a href="#" onClick={this.onSubmitEdit}
className="button amount-submit">
<span></span>Update</a>
</div>
</div>
</div>
)
}
}
class Updater extends React.Component {
constructor() {
super();
this.state = {
amount: 0,
description: "",
_from: true,
_to: false,
date: new Date()
};
}
onAmountChange = e => {
this.setState({
amount: e.target.value
});
};
onDescriptionChange = e => {
this.setState({
description: e.target.value
});
};
onSelectTypeChange = e => {
console.log(e.target.value);
this.setState({
[e.target.value === "from" ? "_from" : "_to"]: true,
[e.target.value !== "from" ? "_from" : "_to"]: false
});
if(e.target.value !== "from" && (this.state.from != null || this.state.from !== "")) {
this.setState({
to: this.state.from,
from: null
});
} else if(e.target.value === "from" && (this.state.to != null || this.state.to !== "")){
this.setState({
from: this.state.to,
to: null
});
}
};
onFromToChange = (e) => {
this.setState({
[this.state._from ? "from" : "to"]: e.target.value
});
}
onSubmitEdit = () => {
Axios.put(
"https://httpbin.org/put",
{
...this.state,
},
{ headers: { "Content-Type": "application/json" } }
)
.then(response => {
// handle Response
})
.catch(err => {
// handle Error
});
};
render() {
return (
<div className="updatercontainer">
<div className="updaterinputs">
<input
onChange={this.onAmountChange}
className="amount"
name="amount"
type="text"
placeholder="Enter Amount"
/>
<input
onChange={this.onDescriptionChange}
className="description"
name="description"
type="text"
placeholder="Enter Description"
/>
</div>
<div className="selectbox">
<select onChange={this.onSelectTypeChange}>
<option value="from">From</option>
<option value="to">To</option>
</select>
<input onChange={this.onFromToChange} className="fromto" type="text"
name="fromto" placeholder="Enter From or To Name"/>
</div>
<div className="selectboxcontainer">
<div onClick={this.onSubmitEdit} className="button-container">
<a href="#" className="button amount-submit">
<span>Update</span>
</a>
</div>
</div>
</div>
);
}
}
Consider Reading More About Handling Inputs, Forms, Events
Working Sandbox!
you just need an onChange event to update the state based on the input values name
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
//On your inputs
<input
className="amount"
name="amount"
type="text"
placeholder="Enter Amount"
value={this.state.amount}
onChange={() => this.handleChange(e)}
/>

How to save input entered by the user in a state which is an array in React

The whole idea is to take users input in a form and display their input in a JSON object. In state I have an array and inside it another array.
My Form.js looks like this,
state= {
groups:[{
typeA:[{}],
typeB:[{}]
}],
credentials: false
};
change = e =>{
this.setState({[e.target.name]: e.target.value})
};
handleSubmit(event) {
event.preventDefault();
this.setState({
credentials: true
});
}
render(){
return(
<div class="classform">
<form >
<label>
Field1:
<br/>
<input type="text"
name="typeA"
placeholder="Type A"
//store it as the first element of the type A
value={this.state.groups.typeA[0]}
onChange={this.change.bind(this)}
/>
//other fields with the same code
Subsequently, Field2 will be stored as the second element of type A
Then, Field3 and Field4 will be stored as 2 of type B array
I expect the code to give me an output like :
"typeA": ["field1 value", "field2 value"],
"typeB": ["field3 value", "field4 value"]}
I'm a beginner with React and I'm not able to store the field values in the state which is an array.
For the sake of simplicity, I will recommend below solution,
Instead of having a nested array in the state, you can manage the input values in the different state variables and at the time of submitting, change them to the output you want.
state = {
field1: '',
field2: '',
field3: '',
field4: '',
}
change = e => {
this.setState({[e.target.name]: e.target.value})
};
handleSubmit(event) {
event.preventDefault();
const { field1, field2, field3, field4 } = this.state;
// This is your output
const output = [{typeA: [field1, field2], typeB: [field2, field3]}];
this.setState({
credentials: true
});
}
render(){
return(
<div class="classform">
<form >
<label>
Field1:
<br/>
<input type="text"
name="field1"
placeholder="Type A"
value={this.state.field1}
onChange={this.change}
/>
</label>
<label>
Field2:
<br/>
<input type="text"
name="field2"
placeholder="Type A"
value={this.state.field2}
onChange={this.change}
/>
</label>
try this:
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
groups: [
{
typeA: [{}],
typeB: [{}]
}
],
credentials: false
};
}
change = (e, key) => {
let { groups } = this.state;
let myVal = groups[0][e.target.name];
// or if you want to add value in an object likr [{value: 'abcd'}]
myVal[0][key] = e.target.value;
groups[0][e.target.name] = myVal;
console.log("TCL: App -> groups", groups);
this.setState({ groups });
};
render() {
return (
<div>
<input
type="text"
name="typeA"
placeholder="Type A"
value={this.state.groups[0].typeA[0].value2}
onChange={e => this.change(e, "value2")}
/>
<input
type="text"
name="typeA"
placeholder="Type A2"
value={this.state.groups[0].typeA[0].value}
onChange={e => this.change(e, "value")}
/>
<br />
<input
type="text"
name="typeB"
placeholder="Type b"
value={this.state.groups[0].typeB[0].value}
onChange={e => this.change(e, "value")}
/>
</div>
);
}
}
Give each input a custom attribute, for example data-group="typeA". In your on change function get that value and add the values to the correct array.
<input
type="text"
name="col2"
placeholder="Type A"
data-group="typeA" // add custom attribute typeA | typeB etc.
onChange={e => this.change(e)}
/>
In your change handle get the custom attribute, and use it to add the value to the correct array.
change = e => {
// create a copy of this.state.groups
const copyGroups = JSON.parse(JSON.stringify(this.state.groups));
// get data-group value
const group = event.target.dataset.group;
if (!copyGroups[0][group]) {
copyGroups[0][group] = []; // add type if it doesn't exists
}
const groups = copyGroups[0][group];
const index = this.findFieldIndex(groups, e.target.name);
if (index < 0) {
// if input doesn't exists add to the array
copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
} else {
// else update the value
copyGroups[0][group][index][e.target.name] = e.target.value;
}
this.setState({ groups: copyGroups });
};
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function formatState(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) => match);
}
const Credentials = ({ value }) => {
return <pre>{formatState(value)}</pre>;
};
class App extends React.Component {
state = {
groups: [
{
typeA: [],
typeB: []
}
],
credentials: false
};
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
// get the current input index in the array typeA | typeB
findFieldIndex = (array, name) => {
return array.findIndex(item => item[name] !== undefined);
};
change = e => {
// create a copy of this.state.groups
const copyGroups = JSON.parse(JSON.stringify(this.state.groups));
// get data-group value
const group = event.target.dataset.group;
if (!copyGroups[0][group]) {
copyGroups[0][group] = []; // add new type
}
const groups = copyGroups[0][group];
const index = this.findFieldIndex(groups, e.target.name);
if (index < 0) {
// if input doesn't exists add to the array
copyGroups[0][group] = [...groups, { [e.target.name]: e.target.value }];
} else {
// update the value
copyGroups[0][group][index][e.target.name] = e.target.value;
}
this.setState({ groups: copyGroups });
};
removeKey = (key) => {
const temp = {...this.state};
delete temp[key];
return temp;
}
render() {
return (
<div>
<input
type="text"
name="col1"
placeholder="Type A"
data-group="typeA"
onChange={e => this.change(e)}
/>
<input
type="text"
name="col2"
placeholder="Type A"
data-group="typeA"
onChange={e => this.change(e)}
/>
<input
type="text"
name="col2"
placeholder="Type B"
data-group="typeB"
onChange={e => this.change(e)}
/>
<input
type="text"
name="typec"
placeholder="Type C | New Type"
data-group="typeC"
onChange={e => this.change(e)}
/>
<input
type="text"
name="typed"
placeholder="Type D | New Type"
data-group="typeD"
onChange={e => this.change(e)}
/>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.removeKey('credentials'), undefined, 2)} />
)}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>

turning a validate function from if statements to a switch statements

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.

Categories