I am using formik in react js.. currently I just added enableReinitialize to update the initialValues after there is any change in state.. so here the validation message are shown even after the correcting the text. This validation message get's removed after clicking outside of textbox. can I hide this message as soon as the the data is corrected in textbox.
Dirty and isValid keeps the button disabled even though there are no validations.
Register.JS
import React, { Component, useState } from 'react';
import {Formik} from 'formik';
import validateSignUp from '../../containers/Validations/SignUp/SignUpValidation'
class Register extends Component {
constructor(props) {
super();
this.state = {
fields: {CountryCode:'', EmailId:'', Password:''},
errors: {},
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
let fields = this.state.fields;
fields[e.target.name] = e.target.value;
this.setState({
fields,
});
}
handleSubmit(){
console.log(this.state.fields);
}
render() {
return (
<Formik
enableReinitialize
initialValues={this.state.fields}
validate={validateSignUp}
>
{({errors, touched, handleBlur, isSubmitting, isValid, dirty}) => (
<div className='signUp-form form_wrapper'>
<div className='form-body'>
<form name='first' onSubmit={this.handleSubmit}>
<div className='row'></div>
<div className='form-group'>
<label>Email address</label>
<input type='email' name="EmailId"
onChange={this.handleChange} placeholder='abc#example.com'
onBlur={handleBlur} value={this.state.fields.EmailId}
className='form-control'/>
<div className='info-message'>
<div className='errorMsg'>{errors.EmailId &&
touched.EmailId && errors.EmailId}</div>
</div>
</div>
<div className='form-group'>
<label>Password</label>
<input type='password' name="Password"
onChange={this.handleChange} onBlur={handleBlur} value={this.state.fields.Password}
className='form-control'/>
<div className='info-message'>
<div className='errorMsg'>{errors.Password &&
touched.Password && errors.Password}</div>
</div>
</div>
<button type='submit' disabled={!(isValid && dirty)} className='btn btn-danger'>
continue
</button>
</form>
</div>
</div>
)}
</Formik>
);
}
}
export default Register;
SignUpValidation.JS
const validateSignUp = validate => {
const errors = {};
if (!validate.EmailId) {
errors.EmailId = 'Please Enter Email ID';
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(validate.EmailId)) {
errors.EmailId = 'Invalid email address';
}
if (!validate.Password) {
errors.Password = 'Please Enter New Password Which you want to set';
} else if (!validate.Password.match(/^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*
[##$%&]).*$/)) {
errors.Password = 'Password Must contain at least one number and one uppercase and lowercase
letter, and at least 8 or more characters';
}
return errors;
};
export default validateSignUp;
I think you should use yup for formik validations as it automatically handle those cases.
Related
I need some help, I am new to react. I created a Functional Component form with Context API and I am trying to prevent it from submitting if the form failed validation and return false. I have check the validation and it is working by uncommenting the html input button and comment out the React Link (<Link></Link>) button. I try the code with class component and bind the handleChange and handleSubmit inside the constructor and it work: however, I do not want to used the class component. I just want to used the function component.
const Employee = () => {
const ctx = useContext(FormActualContext);
const handleChange = (event) => {
ctx.handleChange({
event,
type: 'employee',
});
};
const validate = () => {
const {
firstName,
lastName,
email,
dateOfBirth,
phoneNum,
} = ctx.formData.employee;
const { employee } = ctx.formErrors;
let {
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
} = employee;
firstNameError = !firstName ? 'First name can not be blank' : '';
lastNameError = lastName === '' ? 'Last name can not be blank' : '';
dateOfBirthError = !dateOfBirth ? 'Enter a valid date of birth' : '';
if (!validateEmail(email)) {
emailError =
email === '' ? 'Enter a valid email' : `${email} is not valid email`;
} else {
emailError = '';
}
if (!validatePhoneNumber(phoneNum)) {
phoneNumError =
phoneNum === ''
? 'Enter a valid phone'
: `${phoneNum} is not a valid phone number`;
} else {
phoneNumError = '';
}
if (
firstNameError ||
lastNameError ||
emailError ||
dateOfBirthError ||
phoneNumError
) {
ctx.setFormErrors({
employee: {
...employee,
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
},
});
return false;
}
return true;
};
const handleSubmit = (event) => {
event.preventDefault();
const isValid = validate();
if (isValid) {
ctx.reSetEmployee();
}
};
const {
employee: {
firstNameError,
lastNameError,
emailError,
dateOfBirthError,
phoneNumError,
},
} = ctx.formErrors;
return (
<div className="container_fluid">
<div className="registration_form_container">
<div className="register_context">
<form action="" onSubmit={handleSubmit} className="registration_form">
<div className="form-group">
<input
type="text"
name="firstName"
id="firstName"
placeholder={'Enter first name'}
onChange={handleChange}
/>
<span>{firstNameError}</span>
</div>
<div className="form-group">
<input
type="text"
name="lastName"
id="lastName"
placeholder={'Enter last name'}
onChange={handleChange}
/>
<span>{lastNameError}</span>
</div>
<div className="form-group">
<input
type="text"
name="email"
id="email"
placeholder={'Enter email address'}
onChange={handleChange}
/>
<span>{emailError}</span>
</div>
<div className="form-group">
<input
type="date"
name="dateOfBirth"
id="dateOfBirth"
placeholder={'Enter date of birth'}
onChange={handleChange}
/>
<span>{dateOfBirthError}</span>
</div>
<div className="form-group">
<input
type="text"
name="phoneNum"
id="phoneNum"
placeholder={'Enter phone number (international: +1)'}
onChange={handleChange}
/>
<span>{phoneNumError}</span>
</div>
<div className="form-group custom_btn_container">
{/*<input type="submit" className="btn" value="Register"/>*/}
<Link to="/addressForm">
<input type="submit" className="btn" value="Register" />
</Link>
</div>
</form>
</div>
</div>
</div>
);
};
Issue
The issue isn't that the form is being submitted upon validation (true or false), but rather that both field validation and route navigation are more or less occurring at the same time. The Link, upon being clicked, will immediately navigate to the specified path.
Solution
Seems you want to validate the input, and only upon successful validation, call the reSetEmployee function on the context and navigate to "/addressForm".
I suggest rendering the submit button not within a Link and use imperative navigation. For this I'm assuming you are using react-router-dom library.
import { useHistory } from 'react-router-dom';
...
const history = useHistory();
...
const handleSubmit = (event) => {
event.preventDefault();
const isValid = validate();
if (isValid) {
ctx.reSetEmployee();
history.push("/addressForm");
}
};
...
<div className="form-group custom_btn_container">
<input type="submit" className="btn" value="Register"/>
</div>
I'm creating my first Redux-form but the form doesn't add the info to database.
class UserInfo extends Component{
renderField(field) {
const { meta: { touched, error } } = field;
const className = `form-group ${touched && error ? "has-danger" : ""}`;
return (
<div className={className}>
<label>{field.label}</label>
<input className="form-control" type="text" {...field.input} />
<div className="text-help">
{touched ? error : ""}
</div>
</div>
);
}
onSubmit(values){
//this.props.addUser(values);
console.log(values);
}
render(){
const { handleSubmit, pristine, reset, submitting } = this.props;
return(
<div>
<div>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div className='form-row'>
<Field label='Name' component={this.renderField} name='username' />
<Field label='Address' component={this.renderField }name='address' />
<Field label='Contact' component={this.renderField} name='contact' />
<Field label='Email'component={this.renderField} name='email' />
<button type="submit" className="btn btn-primary">Add new User</button>
</div>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.uername) {
errors.username = 'Please enter a first name';
}
if (!values.address) {
errors.address = 'Please enter an address';
}
if (!values.contact) {
errors.contact = 'Please enter a phone number'
}
if (!values.email) {
errors.email = 'Please enter an email';
}
return errors;
}
export default reduxForm({
validate,
form: 'UserForm'
})(connect(null, { addUser })(UserInfo));
If I use console.log(values) in onSubmit function I don't get any output in the browser.I'm using createLogger to help with actions. And it shows type:
"##redux-form/SET_SUBMIT_FAILED"
I can't figure out what type of error this is.
Use developer tools(ctrl+shift+I), take redux-- state--form--'NameofReduxform'--syncErrors. Then, you might see some required fields.. You need to fill the fields to make the submit work. Hope this helps!
How to provide validations for react js form.
I have taken form with two fields. if two fields are enabled then only need to enable save button.
import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'
const FormExampleForm = () => (
<Form>
<Form.Field>
<label>First Name</label>
<input placeholder='First Name' />
</Form.Field>
<Form.Field>
<label>Last Name</label>
<input placeholder='Last Name' />
</Form.Field>
<Form.Field>
<Checkbox label='I agree to the Terms and Conditions' />
</Form.Field>
<Button type='submit'>Submit</Button>
</Form>
)
export default FormExampleForm
In redux forms 6.7 there's a simple property called pristine which can be used to do this. But if the user enters some value in at least one input field the submit button gets activated. To achieve that you may need to do something like this.
render() {
// const { handleSubmit } = this.props;
const {handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
</form>
);
}
}
But if you need to enable submit button, say when the user inputs values in 2 given fields or so, then this becomes complex. You need to do something like this. Here's my sample usecase. The form has 3 fields firstName, lastName and age. The submit button gets activated only if the user enters values for firstName and lastName input fields here. Please check out the following code.
import React, { Component } from 'react';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
class UserNew extends Component {
constructor(props) {
super(props);
this.isSubmitEnabled = this.isSubmitEnabled.bind(this);
}
onSubmit(values) {
console.log(values);
}
isSubmitEnabled() {
// Access field values here and validate them
const firstName = this.props.firstNameValue;
const lastName = this.props.lastNameValue;
if(firstName && lastName){
return true;
}
return false;
}
render() {
const { handleSubmit } = this.props;
const isEnabled = this.isSubmitEnabled();
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" />
</div>
</div>
<div>
<label>Age</label>
<div>
<Field name="age" component="input" type="text" />
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={!isEnabled}>Submit</button>
</form>
);
}
}
UserNew = reduxForm({
form: 'UserNewForm'
})(
UserNew
);
// Decorate with connect to read form values
const selector = formValueSelector('UserNewForm') // <-- same as form name
UserNew = connect(state => {
const firstNameValue = selector(state, 'firstName')
const lastNameValue = selector(state, 'lastName')
return {
firstNameValue,
lastNameValue,
}
})(UserNew)
export default UserNew;
To access field values you need to use formValueSelector in ReduxForms. For that you need to connect your form to redux store using connect helper.
Hope this helps. Happy coding !
Either you can use Redux-form as its not used in above code, so you can write custom JS validation code. For this you need to follow below
Initialise state in
contructor(){
this.state{
first_name: null,
last_name: null,
}
}
Change state of every input element onChange onChange={()=>this.setState({first_name: event.target.value})}
Now add onClick property to <Button onClick={this.handleSubmit} />
inside handleSubmit() you can get values through local state of component and add validation rules like
if(this.state.first_name =="") {
console.log("Enter first name")
}
I am getting this error in my console.
"Form submission canceled because the form is not connected"
after trying to migrate my redux-form from v5 to v6 since we migrated our app to a more recent version of React.
I am not sure what is going wrong here so i figured I could use a second or third pair of eyes.
Here is my "Smart Component"
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form/immutable';
import { connect } from 'react-redux';
import { logUserIn } from '../../actions/authentication';
import { VALID_EMAIL_REGEX } from '../../config/app_config';
import LoginForm from './LoginForm';
const FORM_ID = 'loginForm';
export class LoginFormContainer extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
loginAction: PropTypes.func.isRequired,
};
performLogin = (params) => {
const { loginAction } = this.props;
const credentials = {
email: params.email,
password: params.password,
};
loginAction(credentials, '/home');
}
render() {
const { handleSubmit, submitting } = this.props;
return (
<LoginForm
handleSubmit={ handleSubmit }
loginFunction={ this.performLogin }
submitting={ submitting }
/>
);
}
}
const validate = values => {
const errors = {};
if (!values.email || values.email === '') {
errors.email = 'Required';
}
else if (!VALID_EMAIL_REGEX.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password || values.password === '') {
errors.password = 'Required';
}
return errors;
};
LoginFormContainer = reduxForm({
form: FORM_ID,
validate,
})(LoginFormContainer);
export default connect(null, {
loginAction: logUserIn,
})(LoginFormContainer);
I am passing down my submission handler function as a prop to my actual form that contains the Field component for inputs. The loginAction will link to the action for redux to send the values to the backend and redirect to home.
import React, { PropTypes } from 'react';
import { Field } from 'redux-form/immutable';
import { getClassName, checkButtonDisabled } from '../../utils/forms';
import { Link } from 'react-router';
const renderInput = (field) => {
return (
<div className={ getClassName(field.meta.touched, field.meta.error) }>
<input
{...field.input}
className="form-control form-control-success"
type={field.type}
/>
{field.meta.touched &&
field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
);
};
export default class LoginForm extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
loginFunction: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
};
render() {
const {
loginFunction,
submitting } = this.props;
return (
<form onSubmit={ loginFunction.bind(this) }>
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</form>
);
}
}
I successfully was able to get the form to work but when I hit login I get the error above and I am redirected to home, but I am not authenticated and I get a 422 error as well. I couldn't tell if my form connecting is the only error or if my action is not getting the right information from the form submission function.
Got any suggestions?
You are redirected home, because your loginFunction() is fired, but the form is not submitted
There are a couple of things that need to be updated. Your <form> tag must have a corresponding id and it should handle submit by passing your function to redux-form inbuilt submit handler. So you modifying LoginForm class as follows should get the form working
<form id="loginForm" onSubmit={ handleSubmit(this.loginFunction.bind(this)) } >
More about internal redux-form method handleSubmit here: http://redux-form.com/6.5.0/docs/api/Form.md/
Using the answer given to me above I just wanted to clarify what I did to solve the issue.
I grabbed the handleSubmit method that comes from the reduxForm and passed it to the LoginForm as a prop from the container that also retrieved it from the props.
I also imported the Form component from redux-form on the LoginForm component and just simply replaced the normal JSX tag with .
here were the final changes I made.
LoginForm.jsx:
//Added Form from redux-form
import { Field, Form } from 'redux-form/immutable';
render() {
const {
handleSubmit,//defined handleSubmit function from props which comes from the reduxForm being exported to the state.
loginFunction,
submitting } = this.props;
return (
//replaced <form></form> with <Form></Form>
<Form id="loginForm" onSubmit={ handleSubmit(loginFunction.bind(this)) }>
//passed my loginFunction into the handleSubmit function
//added an id to <Form> that corresponds to the forms id that was passed in reduxForm({ form: 'loginForm' })
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</Form>
);
It is possible to use the Popup Component to display the Input Errors in react Semantic UI?
Something like this
<Popup
content="Error Message"
trigger={
<Input placeholder='Name' />
}
/>
I think there is a way to achieve that, but not by using the PopUp component. To achieve that see the semantic-ui-react documentation on Forms with Label (pointing).
You can use the logic illustrated in the code below:
import React, { Component } from 'react'
import { Form, Label, Input, Button } from 'semantic-ui-react'
export default class MyCustomForm extends Component {
constructor(props){
super(props)
}
this.state = {
input1: 'some value',
input2: '',
errors: {
input1: 'Input 1 error message'
}
this.onChange = this.onChange.bind(this)
this.validate = this.validate.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e, {name, value}){
const state = this.state
const { errors } = state
if(errors[name]){
delete errors[name]
}
this.setState(Object.assign({},...state,{[name]: value, errors }))
this.validate(name, value)
}
validate(name, value){
{/*
THIS SHOULD VALIDATE THE INPUT WITH THE APPROPRIATE NAME ATTRIBUTE
AND UPDATE THE ERRORS ATTRIBUTE OF THE STATE
*/}
}
onSubmit(e){
e.preventDefault()
{/* CLEAR THE ERRORS OF THE STATE, SUBMIT FORM TO BACKEND, THENj RESET ERRORS IF ANY */}
}
render() {
<Form size='small' key='mycustomform'>
<Form.Group>
<Form.Field error={errors.input1} required>
<label>Input1</label>
<Input name='input1' onChange={this.onChange}/>
{errors.input1 && <Label pointing color='red'>{errors.input1}</Label>}
</Form.Field>
</Form.Group>
<Form.Group>
<Form.Field error={errors.input2}>
<label>Input2</label>
<Input name='input2' onChange={this.onChange}/>
{errors.input2 && <Label pointing color='red'>{errors.input2}</Label>}
</Form.Field>
</Form.Group>
<Form.Field control={Button} onSubmit={this.onSubmit}/>
</Form>
}