I'm using react-bootstrap and I'm trying to validate a form using the validate-state option. I can't figure out how to use getValidationState() to return error when I submit a form which would turn the input field red. Currently when the form loads I get an error message in console ProfileCandidateForm.getValidationState ReferenceError: error is not defined.
If I remove getValidationState() I can submit the form and if it errors the returns the error to an alert box. I would like to change this to the validate-state in bootstrap.
Any help is appreciated. Still wrapping my head around React.
export default class ProfileCandidateForm extends Component {
constructor(props) {
super(props);
var profileCandidate = this.props.profileCandidate;
var firstName = profileCandidate && profileCandidate.name && profileCandidate.name.first;
this.state = {
firstName: firstName || "",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
var profileCandidate = this.state;
insertProfileCandidate.call({profileCandidate}, (error) => {
if (error) {
alert(error.reason);
}
});
}
getValidationState() {
if (error) return 'error';
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<FormGroup
validationState={this.getValidationState()}
>
<FormControl
type="text"
name="firstName"
value={this.state.firstName}
placeholder="First name"
onChange={this.handleChange}
/>
<FormControl.Feedback />
</FormGroup>
<FormGroup >
<Button type="submit">
Save
</Button>
</FormGroup>
</form>
)
}
}
ProfileCandidateForm.propTypes = {
profileCandidate: PropTypes.object.isRequired,
}
You need to reference the value (state) of the text field in the getValidationState() function. Then return null (for no visible validation), 'success' (for green visible validation), or 'error' (for red validation).
Here is one of my getValidationState funcs.
getValidationState() {
var value = this.state.value;
if (value===null || value==='') { return null; }
var valid = this._getValidity(value)
if (valid===true) {
return 'success';
} else {
return 'error';
}
}
Related
Can someone explain why this validation is not working properly? When I click the submit button I should check to see if the field is empty, if it is it should display the error and prevent the next page. If the form has been filled in, then no message should display and clicking the button will take you to a new screen. Currently, the error displays whether or not the field is filled in and the page does not advance in either case.
import React, { Component } from 'react'
const intialState = {
account: "",
accountError: ""
};
export class LandingPage extends Component {
state = intialState;
handleChange = event => {
this.setState({
})
}
continue = e => {
e.preventDefault();
this.props.nextStep();
}
validate = () => {
let accountError= "";
if (!this.state.name) {
accountError = "You must enter an account number!";
}
if (accountError) {
this.setState({ accountError });
return false;
}
return true;
};
handleSubmit = event => {
event.preventDefault();
const isValid = this.validate();
if (isValid) {
console.log(this.state);
this.setState(intialState);
}
};
previous = e => {
e.preventDefault();
this.props.previousStep();
}
render() {
const { values } = this.props;
const { errors } = this.props;
return (
<div>
<h1>Enter an account number</h1>
<input
type="text"
required="required"
placeholder="Enter Account Number"
autoComplete='off'>
</input>
<div className="footer">
<button onClick={this.validate}>Submit</button>
</div>
<div>
{this.state.accountError}
</div>
</div>
)
}
}
export default LandingPage;
The issue is that the name in the state was not getting any value. Fix it like below.
Change the initialState to have the name field
const intialState = {
...
...
name: ""
};
handleChange needs to set the value from the event.
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value });
};
Update the validate function to handle the case when input is typed.
validate = () => {
let accountError = "";
if (!this.state.name) {
accountError = "You must enter an account number!";
}
if (accountError) {
this.setState({ accountError });
return false;
}
this.setState({ accountError: "" });
return true;
};
Add the handleChange as the onChange to the input element.
<input
...
...
...
name="name"
onChange={this.handleChange}
></input>
Working Example
I am trying to build a login form in React.js. I want to enable/disable Login button based on results return by validate method. React throws error 'Invalid value for prop disabled on tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.'. Does anyone have came across same error? Help me to understand what is going wrong here?
import React, { Component } from "react";
import Input from "../common/input";
import Joi from "joi-browser";
class LoginForm extends Component {
state = {
account: {
username: "",
password: "",
},
errors: {},
};
schema = {
username: Joi.string().required().label("Username"),
password: Joi.string().required().label("Password"),
};
abortEarly = {
abortEarly: false,
};
handleSubmit = (event) => {
event.preventDefault();
const errors = this.validate();
if (errors) return;
console.log("submitted");
};
validate = () => {
const result = Joi.validate(
this.state.account,
this.schema,
this.abortEarly
);
const errors = {};
if (!result.error) return null;
result.error.details.map((detail) => {
errors[detail.path[0]] = detail.message;
return detail.path[0];
});
// console.log(errors);
this.setState({ errors });
return errors;
};
validateProperty = ({ name, value }) => {
const propertyTobeValidated = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(propertyTobeValidated, schema);
return error ? error.details[0].message : null;
};
handleChange = ({ currentTarget }) => {
const errors = { ...this.state.errors };
const error = this.validateProperty(currentTarget);
if (error) errors[currentTarget.name] = error;
else delete errors[currentTarget.name];
const account = { ...this.state.account };
account[currentTarget.name] = currentTarget.value;
this.setState({ account, errors });
};
render() {
const { account, errors } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
label="Username"
name="username"
value={account.username}
onChange={this.handleChange}
error={errors.username}
></Input>
<Input
label="Password"
name="password"
value={account.password}
onChange={this.handleChange}
error={errors.password}
></Input>
<button disabled={this.validate} className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
Disabled is a boolean property, meaning it can only have a value of true or false. Instead of a boolean, your validate function is returning an object, thus React throws an "Invalid value" error. In order to fix this you could check if the result of this.validate is null:
<button
disabled={(this.validate() !== null)}
className="btn btn-primary"
>
Login
</button>
Also, you forgot to call your this.validate at all :)
Regarding the "Maximum update depth...", you should remove the this.setState from the this.validate, because you are already putting the error in state in the handleChange method.
Here you go with a solution
import React, { Component } from "react";
import Input from "../common/input";
import Joi from "joi-browser";
class LoginForm extends Component {
state = {
account: {
username: "",
password: "",
},
errors: {},
};
schema = {
username: Joi.string().required().label("Username"),
password: Joi.string().required().label("Password"),
};
abortEarly = {
abortEarly: false,
};
handleSubmit = (event) => {
event.preventDefault();
const errors = this.validate();
if (errors) return;
console.log("submitted");
};
validate = () => {
const result = Joi.validate(
this.state.account,
this.schema,
this.abortEarly
);
const errors = {};
if (!result.error) return false;
result.error.details.map((detail) => {
errors[detail.path[0]] = detail.message;
return detail.path[0];
});
// console.log(errors);
this.setState({ errors });
return true;
};
validateProperty = ({ name, value }) => {
const propertyTobeValidated = { [name]: value };
const schema = { [name]: this.schema[name] };
const { error } = Joi.validate(propertyTobeValidated, schema);
return error ? error.details[0].message : null;
};
handleChange = ({ currentTarget }) => {
const errors = { ...this.state.errors };
const error = this.validateProperty(currentTarget);
if (error) errors[currentTarget.name] = error;
else delete errors[currentTarget.name];
const account = { ...this.state.account };
account[currentTarget.name] = currentTarget.value;
this.setState({ account, errors });
};
render() {
const { account, errors } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
label="Username"
name="username"
value={account.username}
onChange={this.handleChange}
error={errors.username}
></Input>
<Input
label="Password"
name="password"
value={account.password}
onChange={this.handleChange}
error={errors.password}
></Input>
<button disabled={this.validate} className="btn btn-primary">
Login
</button>
</form>
</div>
);
}
}
export default LoginForm;
validate method should return boolean (true or false)
React Redux form input editing returns error message of an uncontrolled input
Below code uses react redux to display a record in a form input from database and it worked fine. when I tried to edit the form input for lastname it does not edit but rather
it displays error
A component is changing an uncontrolled input of type text to be controlled.
Input elements should not switch from uncontrolled to controlled (or vice versa).
Decide between using a controlled or uncontrolled input element for the lifetime of the component.
here is how am displaying the result that i want to edit in a form input
<input type="text" className="form-control" value={pgs1.items1 && this.props.pgs1.items1[0].lastName} onChange={this.handleChange}/>
handleChange = (name) => (event) => {
this.setState({ [name]: event.target.value });
}
/*
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
*/
below is the full react redux code
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class Edit1 extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
lastName: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.dispatch(userActions.getAll_Rec());
}
handleChange = (name) => (event) => {
this.setState({ [name]: event.target.value });
}
/*
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
*/
handleSubmit(event) {
event.preventDefault();
//process form submit
}
render() {
const { pg1, pgs1 } = this.props;
return (
<div className="list">
<div >
Name: {pgs1.items1 && this.props.pgs1.items1[0].lastName}<br />
<input type="text" className="form-control" value={pgs1.items1 && this.props.pgs1.items1[0].lastName} onChange={this.handleChange}/>
</div>
</div>
);
}
}
function mapStateToProps(state) {
const { pgs1 } = state;
const { pg1 } = state;
return {
pg1,
pgs1
};
}
const connectedEdit1 = connect(mapStateToProps)(Edit1);
export { connectedEdit1 as Edit1 };
At last what solve my problem of input form not editing was replacing form input value{} with defaultValue{} option. I leverage solutions/answer posted by Solanki here React Input Type not editable
Not this anymore
<input type="text" className="form-control" value={pgs1.items1 && this.props.pgs1.items1[0].lastName} onChange={this.handleChange}/>
but this solve my problem
<input type="text" className="form-control" defaultValue={pgs1.items1 && this.props.pgs1.items1[0].lastName} onChange={this.handleChange}/>
With implementation of defaultValue{} I can now edit form values all the time.
In my React app, I have a component call <ComingSoonForm/> Inside that, I have a TextInputField. If the Email valid, the Notify-Button is able. If the Email is invalid, the Notify-Button is disabled.
Here is my Component File:
import React, { Component } from "react";
import { TextInputField, toaster, Button, } from "evergreen-ui";
import Box from 'ui-box';
import { validateEmail } from "../FormValidation/FormValidator";
class ComingSoonForm extends Component {
constructor(props) {
super(props);
this.state = {
emailErr: {
status: true,
value: ""
},
email: "",
isDisabled: true,
};
this.handleSubmit = this.handleSubmit.bind(this);
this.checkFormStatus = this.checkFormStatus.bind(this);
}
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email});
console.log(this.state.email);
this.checkFormStatus();
};
handleSubmit() {
if (this.checkFormStatus()) {
alert("Form is OK")
}
}
checkFormStatus() {
// form validation middleware
const { email } = this.state;
const emailErr = validateEmail(email);
if (!emailErr.status) {
this.setState({isDisabled:false})
return true;
} else {
this.setState({
emailErr,
});
return false;
}
}
render() {
return (
<div>
<Box className="welcomePageWelcomeInnerLoginButton">
<TextInputField
marginTop={15}
width={200}
onChange={this.handleEmailInput}
value={this.state.email}
type="email"
placeholder="Your email-address"
inputHeight={40}
/>
</Box>
<Button height="40" appearance="primary" marginBottom={5} className="welcomePageWelcomeInnerLoginButtonWidth" disabled={this.state.isDisabled} onClick={this.handleSubmit}>Notify Me</Button>
</div>
);
}
}
export default ComingSoonForm;
But this case doesn't work correctly. So when the command console.log(this.state.email) in the handleEmailInput Function run, I get the following data in the console:
I type one letter (t) and I get:
//ComingSoonForm.js:25
I type a second letter (t) and I get:
t //ComingSoonForm.js:25
t //FormValidator.js:10
Why do I have to enter two letters in order for one to appear in the console?
setState is asynchronous, you can pass a callback method as a second parameter like this:
handleEmailInput = e => {
const email = e.target.value;
this.setState({ email: email }, () => console.log(this.state.email));
this.checkFormStatus();
};
I'm trying to pre-populate a form with data from my mongoDB. I think I'm supposed to use componentDidUpdate() to setState the state of the form field, however, the page goes into an infinite loop. Very new to react so this might be an obvious question, I just haven't been able to find a solution.
Path: ContactDetailsComponent.js
export default class ContactDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
'name.first': '',
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentWillReceiveProps(nextProps) {
var profileCandidate = this.props.profileCandidate;
var firstName = profileCandidate && profileCandidate.name && profileCandidate.name.first;
this.setState({
'name.first': firstName,
})
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit} noValidate>
<input
name="name.first"
type="text"
value={this.state['name.first']}
onChange={this.handleInputChange}
className={this.state.errors['name.first'] ? "validate invalid" : "validate" }
/>
</form>
</div>
)
}
}
ContactDetails.propTypes = {
profileCandidate: PropTypes.object,
};
You can slightly modify you following method
componentWillReceiveProps(nextProps) {
var profileCandidate = nextProps.profileCandidate;
var firstName = profileCandidate && profileCandidate.name && profileCandidate.name.first;
this.setState({
'name.first': firstName,
})
}
This will set the updated props to state.
Thanks