React onChange method not working in login form - javascript

I'm facing this problem while trying to move input value to component state hook using the onChange attribute.
i'll be happy with your help.
import React from 'react'
import { useState } from 'react';
import './userInterface.css';
function UserInterface() {
const [userInfo, setUserInfo] = useState({
isLoggedIn : '',
userName : '',
email : '',
password : ''
})
let usenamePattern = /[A-Za-z0-9]{3,16}./ ;
let emailPattern = /[A-Za-z0-9#.]{7,}/ ;
const getInputToState = (e,inputField) => {
switch(inputField){
case 'username' : {
setUserInfo(userInfo.userName = e.target.value)
console.log(userInfo)
break
}
case 'email' : {
setUserInfo(userInfo.email = e.target.value)
console.log(userInfo)
break
}
case 'password' : {
setUserInfo(userInfo.password = e.target.value)
console.log(userInfo)
break
}
default:
return null
}
console.log(userInfo)
}
const alertForm = () => {
if(userInfo.userName == '' && userInfo.email == '' && userInfo.password == ''){
return{
msg : '',
color : 'green'
}
}
else if(userInfo.userName.match(usenamePattern)){
return {
msg : 'You are allright !',
color : 'limegreen'
}
}else if(!userInfo.userName.match(usenamePattern)){
return {
msg : 'Username should be more than 3 characters and less than 16, and contains only alphabets and numbers',
color : 'red'
}
}
}
return (
<div id='user-div'>
<form id='user-form'>
<h2 id="form-title">Tell us who you are :)</h2>
<ul id="form-inputs">
<li className="form-input">
<input type="text" className="user-input" placeholder='Enter a username' maxLength={16} onChange={getInputToState()}/>
</li>
<li className="form-input">
<input type="text" className="user-input" placeholder='Enter your e-mail' onChange={getInputToState()}/>
</li>
<li className="form-input">
<input type="text" className="user-input" placeholder='Create a password' onChange={(e) => {getInputToState()}}/>
</li>
<li className="form-input">
<a><button className='action-form' id='submit-button' disabled>Submit</button></a>
</li>
</ul>
<h4 id='alert-msg' style={{color : alertForm()?.color}}>{alertForm()?.msg}</h4>
<h3 id='login-sign'>Already have an account ? <a href="/" id='login-form'>Log In</a></h3>
</form>
</div>
)
}
export default UserInterface ;
here is the entire component code, it has no relation with props or exports.
When i write in inputs the console returns nothing which means that the function do not work

The first you should do is change
onChange={(e) => {getInputToState()}}
to
onChange={(e) => {() => getInputToState( e, 'your_field_name' )}}
And inside your getInputToState function you should pass to setUserInfo new userInfo object with mutated fields or one field. Hope this will help.
Your function should look like this:
const getInputToState = (e, inputField) =>
{
const newUserInfo = userInfo; // create new state object
userInfo[inputField] = e.target.value; // Change th value
setUserInfo(newUserInfo); // Set new state
};
You also can get rid of the switch statement.

The easiest way to solve this would be to use:
onChange={(e) => getInputToState(e, "username")}
onChange={(e) => getInputToState(e, "email")}
onChange={(e) => getInputToState(e, "password")}
The switch statement will not default to null and give the desired results.
Also see this stackoverflow question.

Related

How to know if form input is empty (React Hooks)

I have a form where I want to know if the input values ​​are empty when onSubmit, they are not sent. I have tried to do it through the if of handleInputChange but this isn't working:
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
if ((e.target as HTMLInputElement).value) {
setNewPost({
...newPost,
[(e.target as HTMLInputElement).name]: (e.target as HTMLInputElement).value
})
}
e.preventDefault();
};
All the code:
const New: React.FC = () => {
// const [newPost, setNewPost] = useState("");
const [newPost, setNewPost] = useState({
title: '',
author: '',
content: ''
})
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
if ((e.target as HTMLInputElement).value) {
setNewPost({
...newPost,
[(e.target as HTMLInputElement).name]: (e.target as HTMLInputElement).value
})
}
e.preventDefault();
};
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //Detiene el formulario para que no actualize la página
setPost(newPost)
}
return (
<div className="containerHomepage">
<form className="formulari" onSubmit={createPost}>
<div className="containerBreadCrumb">
<ul className="breadCrumb">
<li>Posts</li>
</ul>
</div>
<div className="containerTitleButton">
<input
className=""
type="text"
placeholder='Post title'
name="title"
onChange={handleInputChange}
></input>
<button
className="button"
type="submit"
>Save</button>
</div>
<div className="containerEdit">
<input
className=""
type="text"
placeholder='Author'
name="author"
onChange={handleInputChange}
></input>
<input
className=""
type="text"
placeholder='Content'
name="content"
onChange={handleInputChange}
></input>
</div>
</form>
</div>
);
};
// ========================================
export default New;
Your current handleInputChange makes it so that the user cannot change any input to an empty string. There's a major usability flaw here. Once the user types the first character, they cannot delete it! You should allow the inputs to be empty, but disallow submitting the form unless all fields are filled out.
You can use e.currentTarget instead of e.target to avoid a lot of type assertions. There is more information in this question, but what's important here is that e.currentTarget will always be the HTMLInputElement.
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
setNewPost({
...newPost,
[e.currentTarget.name]: e.currentTarget.value
});
};
#rax's answer is on the right track, but I'm going to go further down that path.
An any point in time, you can know whether the form is valid or not by looking at the current state of newPost. There are lots of ways to write this, which all do the same thing:
const isValid = Boolean(newPost.title && newPost.author && newPost.content);
Using type coercion. All strings are truthy except for the empty string.
const isValid = newPost.title !== '' && newPost.author !== '' && newPost.content !== '';
From #Vladimir Trotsenko's answer.
const isValid = Object.values(newPost).every(value => value.length > 0)
Looping over all values of newPost so you don't need to change anything if you add an extra field.
You can use this isValid variable to conditionally disable the "Save" button.
<button type="submit" disabled={!isValid}>Save</button>
You can also use isValid to show messages or other visible feedback to the user. For example, you can show a message when hovering over the disabled button which tells them why it has been disabled.
<button
type="submit"
disabled={!isValid}
title={isValid ? "Create your post" : "All fields must be filled out."}
>
Save
</button>
I'm checking if (isValid) in the createPost function to be on the safe side, but I believe that this is not actually necessary as the form won't be submitted (even when hitting Enter) if the submit button is disabled.
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); // stop the form from reloading the page.
if (isValid) {
// put your actual code here instead.
alert("submit success");
}
};
Complete code:
import React, { useState } from "react";
const New: React.FC = () => {
const initialState = {
title: "",
author: "",
content: ""
};
const [newPost, setNewPost] = useState(initialState);
const isValid = Boolean(newPost.title && newPost.author && newPost.content);
const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
setNewPost({
...newPost,
[e.currentTarget.name]: e.currentTarget.value
});
};
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //stop the form from reloading the page
if (isValid) {
alert("submit success");
}
};
return (
<div className="containerHomepage">
<form className="formulari" onSubmit={createPost}>
<div className="containerBreadCrumb">
<ul className="breadCrumb">
<li>Posts</li>
</ul>
</div>
<div className="containerTitleButton">
<input
className=""
type="text"
placeholder="Post title"
name="title"
onChange={handleInputChange}
value={newPost.title}
/>
<button
className="button"
type="submit"
disabled={!isValid}
title={
isValid ? "Create your post" : "All fields must be filled out."
}
>
Save
</button>
</div>
<div className="containerEdit">
<input
className=""
type="text"
placeholder="Author"
name="author"
onChange={handleInputChange}
value={newPost.author}
/>
<input
className=""
type="text"
placeholder="Content"
name="content"
onChange={handleInputChange}
value={newPost.content}
/>
</div>
</form>
</div>
);
};
export default New;
CodeSandbox
You can compare your input value to empty string like that :
inputValue === ''
or the size of the string :
inputValue.length === 0
And check the value in if statement with inside your submit.
You can validate the empty field inside createPost which should look something like:
const createPost = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); //stop the form from reloading the page
if (!newPost.title || !newPost.author || !newPost.content) {
//show some error message
} else {
//perform further action
setPost(newPost);
}
}
For a full working example click here.
When you try to submit, firstly, you need to check whether input values are present. In order to do that you check each input value to equal empty string.
For a better visualization, create variable that would be responsible for that:
const isValid = newPost.title !== '' && newPost.author !== '' && newPost.content !== ''
Now, if isValid is true, we submit the form, if not, we do not.
const createPost = (e) => {
e.preventDefault()
if (isValid) {
// make api request
} else {
// handle empty fields
}
}

How to add validation check to input field when we have value present in field?

I have a form with 3 input filed FIRST NAME, LAST NAME, NUMBER. So once forms open there is a particular set of pre filled values coming from backend. So, if i try to add validation check for my firstname and if user makes the filed blank and click submit it shows me undefined in console for the respective firstname value. So, could someone help to add validation check for firstname, last name and number ??
const EditContact = inject('Contacts')(observer((props) => {
const { text, Contactfirst, apptContacts } = props;
const { updateContact } = apptContacts;
const CMS = text.appointmentManager.editAppointmentContact;
const [state, setState] = React.useState({
data: {
firstName: Contactfirst[0],
lastName: Contactfirst[1],
number: Contactfirst[2],
},
firstNameValid: '',
lastNameValid: '',
numberValid: '',
});
const handleTextareaChange = (event) => {
const { data } = state;
data[event.target.name] = event.target.value;
setState({
data,
});
};
const valid = () => {
if (state.data.firstName !== 'undefined') {
setState({ firstNameValid: 'Required.Please enter your given name.' });
}
};
const saveButton = () => {
if (valid()) {
const personName = `${state.data.firstName} ${state.data.lastName}`;
const primaryContact = {
name: personName,
phoneNumber: state.data.number,
};
}
};
return (
<div>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing4x} />
<h1 tabIndex="-1" className="HeadingB mt-sheet-heading">
{CMS.heading1}
</h1>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="givenName" name="firstName" label={CMS.name} onChange={handleTextareaChange} value={(state.data.firstName !== 'undefined') ? state.data.firstName : ''} />
<p>{state.firstNameValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="familyName" name="lastName" label={CMS.familyName} onChange={handleTextareaChange} value={(state.data.lastName !== 'undefined') ? state.data.lastName : ''} />
<p>{state.lastNameValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing3x} />
<TextField id="mobileNumber" name="number" label={CMS.mobile} onChange={handleTextareaChange} value={(state.data.number !== 'undefined') ? state.data.number : ''} />
<p>{state.numberValid}</p>
<VerticalSpacing size={ABLE_SPACING_SIZE.spacing4x} />
<ActionButton className={styles.saveCta} variant="HighEmphasis" label={CMS.saveCTA} onClick={() => saveButton()} />
</div>
);
}));
export default EditContact ;
Handling form state and validation is a problem many of us have faced. If you are having trouble solving the issue on your own I would suggest taking look at a library that handles the heavy lifting for you. One such library is Formik https://formik.org/docs/overview
Here is a minimal example that illustrates it in action:
https://codesandbox.io/s/zkrk5yldz
If you want to use material ui for your components that is easy to apply validation to your text fields.
Just follow this link
https://mui.com/components/text-fields/#validation

React form wrong radio values

I have this modalWindow Component, with a form with a preselected "small" option:
import React from "react";
import pizzaStore from "./stores/PizzaStore";
import { observer } from "mobx-react-lite";
import cartStore from "./stores/CartStore";
import { action } from "mobx";
function ModalWindowComponent({ activeModal, setActiveModal }: any) {
const [price, setPrice] = React.useState(pizzaStore.modalProps.price);
console.log(price);
const handlePriceChange = (opt: string) => {
opt === "small"
? setPrice(pizzaStore.modalProps.price)
: opt === "medium"
? setPrice(pizzaStore.modalProps.price * 1.5)
: setPrice(pizzaStore.modalProps.price * 2);
};
const [selectedOption, setSelectedOption] = React.useState("small");
const setClose = () => {
setSelectedOption("small");
setActiveModal(false);
};
let fixedSize = pizzaStore.size;
let size =
selectedOption === "small"
? fixedSize
: selectedOption === "medium"
? fixedSize * 1.5
: fixedSize * 2;
let obj = {
modalName: pizzaStore.modalProps.name,
modalDesc: pizzaStore.modalProps.description,
modalSize: size,
modalPrice: price,
modalImage: pizzaStore.modalProps.imageUrl,
};
return (
<div
className={activeModal ? "modal active" : "modal"}
onClick={() => {
setActiveModal(false);
setSelectedOption("small");
}}
>
<div
className="modal-content"
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="modal-content-header">
<button onClick={() => setClose()}>Close</button>
</div>
<img
src={pizzaStore.modalProps.imageUrl}
className="modal-content-img"
/>
<p className="modal-content-pizza-name">{pizzaStore.modalProps.name}</p>
<p className="modal-content-pizza-desc">
{pizzaStore.modalProps.description}
</p>
<p className="modal-content-pizza-size">{size}см</p>
<p className="modal-content-pizza-weight">
{pizzaStore.setWeight(selectedOption)}грамм
</p>
<p className="modal-content-pizza-price">{price}Руб.</p>
<form
className="modal-content-sizes-form"
onSubmit={(e: any) => {
cartStore.handleSubmitForm(e, obj);
}}
>
<label>
<input
name="radio-size"
value="small"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(selectedOption);
}}
checked={selectedOption === "small"}
className="modal-content-sizes-form-option"
/>
Маленькая
</label>
<label>
<input
name="radio-size"
value="medium"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(selectedOption);
}}
checked={selectedOption === "medium"}
className="modal-content-sizes-form-option"
/>
Средняя
</label>
<label>
<input
name="radio-size"
value="big"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
}}
checked={selectedOption === "big"}
className="modal-content-sizes-form-option"
/>
Большая
</label>
<button
onClick={() => {
setClose();
console.log(cartStore.cartItems);
}}
>
Добавить
</button>
</form>
</div>
</div>
);
}
export default observer(ModalWindowComponent);
The selectedOption state should update when a radiobutton is clicked.
however, if I try to log in to the console it gives the wrong values.
For example, when you click the medium valued radio button the console logs "small". The other problem is that the price state doesn't update accordingly with the selected option state. I don't quite understand what is wrong.
That's because state update is batching and asynchronous
You setSelectedOption and handlePriceChange in the same function which cause the issue that you won't get the latest update selectedOption
So you would use the original value like so:
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(e.target.value);
}}
Or having a useEffect waiting for selectedOption to change before calling handlePriceChange:
useEffect(() => {
handlePriceChange(selectedOption);
}, [selectedOption]);
setSelectedOption actually doesn't change the value of selectedOption in your onChange handler. My guess is, it always logs the previous value to the console.
To fix this, store e.target.value in a variable and log that.

React Form: How to add error message that disappear if the input was typed in

I already built the form in React and it shows the input fields in red borders that'll change to regular borders once someone types it in. I used this example from this React form article link So everything is working except I wanted to add the error message under the input field that displays "Please fill in the blank field" that will disappear once someone starts typing in the field. How do I do this?
Here's my code in Form.js:
import React, { Component } from 'react';
import FormField from './FormFieldBox';
function validate(name, isin) {
// true means invalid, so our conditions got reversed
return {
name: name.length === 0,
isin: isin.length === 0
};
}
export default class PopupForm extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
isin: '',
country: '',
errormessage: ''
}
}
updateInput = (e) =>{
this.setState({[e.target.name]: e.target.value})
}
closePopupSubmit = (e) => {
if (!this.canBeSubmitted()) {
e.preventDefault();
}
let security = { //1.gather security data from form submit
name: this.state.name,
isin: this.state.isin,
country: this.state.country
}
this.props.submitPopup(security); //2.closePopup function, add security data
}
canBeSubmitted() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return !isDisabled;
}
cancelPopupSubmit = (e) => {
e.preventDefault()
this.props.cancelPopup();
}
render() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return (
<div className='popup'>
<div className='popup-inner'>
<form onSubmit={this.closePopupSubmit}>
<FormField onChange={this.updateInput} className={errors.name ? "input error" : "input"} label="Name" type="text" name="name" value={this.state.name} />
<FormField onChange={this.updateInput} className={errors.isin ? "input error" : "input"} label="ISIN" type="text" name="isin" value={this.state.isin} />
<FormField onChange={this.updateInput} label="Country" type="text" name="country" value={this.state.country} />
<button type="button" onClick={this.cancelPopupSubmit} className="button">Cancel</button>
<button type="submit" className="button" disabled={isDisabled}>Submit</button>
</form>
</div>
</div>
)
}
}
And my component FormField.js
import React from "react";
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
{/* {props.errormessage} */}
</div>
</div>
)
}
export default FormBox;
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
</div>
{Boolean(props.value.length) || (
<div className="err-msg">
Please fill in the blank field
</div>
)}
</div>
)
}
There are two ways you can achieve this
First : oninvalid attribute in HTML5 and calling a custom function on that.
Second : along with each element name object in state have a length attribute. In validation function you can check for the length and throw a custom error that you want to display.

Why do my error messages not update when a field is filled out? (React)

This seems like a simple thing to do but after much fiddling, I can't figure out what's wrong. I'm a noob to React so forgive me.
I have a form for logging in. Like most login forms, it's asking for a username and password. Then it has a button to submit. My understanding is that a component will re-render if the state is changed. I have onchange events on each input field that updates the state. So if the field is empty, I press the button to submit, I would expect that the error will show. If I fill in a field, I would expect the error message to go away because the state changed. Am I misunderstanding?
Here is my event handler:
handleLogin(event) {
event.preventDefault();
if (this.state.username == '') {
this.setState({usernameError: "Need to enter a username"})
return;
}
if (this.state.password == '') {
this.setState({passwordError: "Need to enter a password"})
return;
}
}
And the form:
render() {
return(
<form className="login-form">
<h1 className="login-form__header"><FontAwesomeIcon icon="key" className="registration-form__icon"/><i className="fal fa-route-highway"></i>Log Into Your Account</h1>
<input type="text" name="username" placeholder="Username" className="login-form__input" onChange={(event,newValue) => this.setState({username:newValue})}/>
{this.state.usernameError &&
<p class="login-form__error"><FontAwesomeIcon icon="times-circle"/> {this.state.usernameError}</p>
}
<input type="password" name="password" placeholder="Password" className="login-form__input" onChange={(event,newValue) => this.setState({password:newValue})}/>
{this.state.passwordError &&
<p class="login-form__error"><FontAwesomeIcon icon="times-circle"/> {this.state.passwordError}</p>
}
<button className="login-form__button" onClick={this.handleLogin}>Log Into Your Account</button>
</form>
);
}
Right, but you never configured any logic to clear the errors if the field is not empty. Currently, there isnt any logic set-up to turn usernameError and passwordError back to an empty-string or null value.
You might be under the impression that the state is cleared when you re-render but that is not the case. The state-object prior to the re-render still persists, only changing the key-value pair(s) you last updated within this.setState().
Try this:
handleLogin(event) {
event.preventDefault();
const { username, password } = this.state
this.setState({
...this.state,
usernameError: username.length > 0 ? "" : "Need to enter a username",
passwordError: password.length > 0 ? "" : "Need to enter a password"
})
}
Here's a working sandbox with a sligtly modified version of your code. (I removed the FontAwesomeIcons). https://codesandbox.io/s/cool-meninsky-y9r4y
First of all, onChange={(event,newValue) => this.setState({username:newValue})} this is not a good approach, as it will create a new function on every render. So I would suggest to create a dedicated function like -
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
// Make sure the name is as per with your html username password and update the state accordingly
this.setState({
[name]: value
});
}
Do remember to reset the usernameError and passwordError properties of your state on each related onChange event. Otherwise the error message will persist on HTML.
Hi you can try the below code
function validate(email, username, password) {
// true means invalid, so our conditions got reversed
return {
username: username.length === 0, //true if username is empty
password: password.length === 0, //true if password is empty
};
}
class LoginForm extends React.Component {
constructor() {
super();
this.state = {
username: '',
password: '',
touched: {
username: false,
password: false,
},
};
}
handleUsernameChange = (evt) => {
this.setState({ username: evt.target.value });
}
handlePasswordChange = (evt) => {
this.setState({ password: evt.target.value });
}
handleBlur = (field) => (evt) => {
this.setState({
touched: { ...this.state.touched, [field]: true },
});
}
handleSubmit = (evt) => {
if (!this.canBeSubmitted()) {
evt.preventDefault();
return;
}
const { username, password } = this.state;
alert(`Logined: ${email} password: ${password}`);
}
canBeSubmitted() {
const errors = validate(this.state.username, this.state.password);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return !isDisabled;
}
render() {
const errors = validate(this.state.username, this.state.password);
const isDisabled = Object.keys(errors).some(x => errors[x]);
const shouldMarkError = (field) => {
const hasError = errors[field];
const shouldShow = this.state.touched[field];
return hasError ? shouldShow : false;
};
return (
<form className="login-form" onSubmit={this.handleSubmit}>
<h1 className="login-form__header">
<FontAwesomeIcon icon="key" className="registration-form__icon"/>
<i className="fal fa-route-highway"></i>Log Into Your Account
</h1>
<input
className={shouldMarkError('username') ? "error" : ""}
type="text" name="username" placeholder="Username"
value={this.state.username}
onBlur={this.handleBlur('username')
onChange={this.handleUsernameChange} />
<p className={shouldMarkError('username') ? "error" : "hidden"}>
<FontAwesomeIcon icon="times-circle"/> Need to enter a username
</p>
<input type="password" name="password" placeholder="Password"
className={shouldMarkError('password') ? "error" : ""}
value={this.state.password}
onChange={this.handlePasswordChange}
onBlur={this.handleBlur('password')} />
<p className={shouldMarkError('password') ? "error" : "hidden"}>
<FontAwesomeIcon icon="times-circle"/> Need to enter a password
</p>
<button disabled={isDisabled} className="login-form__button"
onClick={this.handleLogin}>Log Into Your Account</button>
</form>
)
}
}
Hope the code could helpful for you.
You can also refer the Demo Validation for Signup
As mentioned by #Satyaki onChange={(event,newValue) => this.setState({username:newValue})} is not a good option you should define separate method for this, alternatively you can change your code to as follows:
onChange={(event,newValue) => this.setState({username:newValue})}, () => {})
This will make sure to complete the lifecycle of setState.

Categories

Resources