I am new to react and created a login form with a few set of validations to raise errors in case of blank username or password. On the very first instance, when nothing is inserted and the Submit is clicked, it works fine. But once the value is changed with a value inserted by the user and then deleting from the text input, the variable gets a value of "" and never gets back to null value. This avoids my special treatment where I inserted !this.state.username. It gets stuck at that moment and the form submission goes anyway to the server. I have tried trim as well but it didn't work
class LoginClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
username: this.props.username,
password: this.props.password,
errorUsername: null,
errorPassword: null,
};
this.handleValidation = this.handleValidation.bind(this);
this.handleChange = this.handleChange.bind(this);
}
//assign textbox values to props
handleChange = (e) => {
this.setState({
[e.target.name]: [e.target.value],
});
};
//handle input validation
handleValidation = (event) => {
if (!this.state.username) {
this.setState({ errorUsername: "Please enter User Name" });
event.preventDefault();
}
if (!this.state.password) {
this.setState({ errorPassword: "Please enter Password" });
event.preventDefault();
}
Your handleChange is incorrect, you should remove the brackets around value:
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});
};
for this scenario I would go with function components and hooks, but for the sake of simplicity and not knowing what the rest of your implementation looks like, I'd go as follows.
Assuming you are triggering handleValidation either on field blur or on submit click, try modifying it like so:
handleValidation = (event) => {
event.preventDefault();
let errorUsername = null;
let errorPassword = null;
if (event.target.name === 'username') {
errorUsername = (event.target.value === '') ? "Please enter User Name" : null;
} else if (event.target.name === 'password') {
errorPassword = (event.target.value === '') ? "Please enter Password" : null;
}
this.setState({ errorUsername, errorPassword });
}
Let me know if you need any further assistance.
Cheers! 🍻
Related
My problem is this ... I am using a form, however, when I change from one field to another, the value referring to the state of the previous field is lost. With that, at the end of the filling, only the last field of the state is filled.
I solved the problem by including a local variable in onSubmit with the fields filled in, but from what I understand so far, the best practice would be to use state.
Could someone help me and tell me what I am failing to do? Thank you very much.
class AddItem extends React.Component {
constructor(props){
super(props);
this.state = {validated: false};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
item: {
titulo: '',
descricao: '',
estado: '',
cidade: null,
usuario: 1,
data: new Date(),
}
}
}
handleSubmit (event) {
const form = event.currentTarget;
console.log(item);
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}else{
api(this.state.item);
event.preventDefault();
}
this.setState({validated: true});
};
handleChange (event) {
let name = event.target.name;
let value = event.target.value;
this.setState({item: {[name]: value}});
};
You're not keeping any of the previous values in state: eg this.setState({validated: true}); will mean that the state no longer has an item property.
Spread the previous state into the new state so that old properties don't get lost whenever you call setState:
this.setState({validated: true});
to
this.setState({ ...this.state, validated: true });
and
this.setState({item: {[name]: value}});
to
this.setState({ ...this.state, item: { ...this.state.item, [name]: value }} );
I am new to react.
For e.g, input value entered 1,2,3,4 and after the event onChange it takes only numbers, then I can
remove 4,3,2 with backspace but not 1. And in HTML DOM also, the 1 cannot be removed.
class House extends Component {
state = {
room: null,
};
componentDidMount() {
if (this.props.house.rent) {
this.setState({ rent: this.props.house.rent });
}
}
onChange = (field, value, mutate) => {
if (field === "houseroom") {
value = parseInt(value.replace(/[#,]/g, ""));
}
mutate({
variables: {
},
});
this.setState({
[field]: value,
});
};
render(){
const {house} = this.props;
<SomeInput
type="text"
value={
(house.room&&
`$${house.room.toLocaleString("en")}`) ||
""
}
onChange={e => {
e.target.placeholder = "Room";
this.onChange("houseroom", e.target.value, mutate);
}}
}
/>
}
It look like a lot of a problem during the update of the state probably due to overrendering elsewhere in the component try to use prevState instead do ensure that state updating can not conflict.
this.setState(prevState => {
[field]:value;
});
Keep me in touch with the result.
Hope this helps someone !
Need to mention "this.state.room" in the input Value and check the prev state using the componentDidUpdate then "this.setState" here and also finally "this.setState" during the event change. Thanks all
How to validate form's input value on user typing or on change? I am trying to read state but it is kind of late/ not realtime.
I am thinking of using a class variable/ property and mutate it, but I am afraid that it will offend the React's principal.
Is there a proper way to create realtime form validation like this in React?
Validation is so widely used that we can find dozens of good ways to do that with react. I like to use the following:
Instead of just hold the value of your inputs on state, you could make a more complex object for each one. Let's begin defining a form with 2 inputs: name and age. The first step would be describe the form in state. Something like that:
state = {
form:{
name:{
value : '',
valid : true,
rules:{
minLength : 3
}
},
age:{
value : '',
valid : true,
rules:{
isNumber : true
}
}
}
}
There we have it! We now have 2 inputs that are valid on the initial render and have their own validation rules(isNumber, minLength). Now we need to write a function that validates the state on the fly. Let's write it then:
onChangeHandler = (key, value) =>{
this.setState(state =>({
...state,
form:{
...state.form,
[key]:{
...state.form[key],
value,
valid : validate(value, state.form[key].rules)
}
}
}))
}
Now we have a form described in state and a handler that updates the state onChange and validate the value of the input on each call. Now the only thing to do is write your validate() function and you are ready to go.
validate = (value, rules) => {
let valid = true
for (let key in rules) {
switch (key) {
case 'minLength':
valid = valid && minLengthValidator(value, rules[key])
break
case 'isNumber':
valid = valid && isNumberValidator(value)
break
default: break
}
}
return valid
}
Now the validators...
minLengthValidator = (value, rule) => (value.length >= rule)
isNumberValidator = value => !isNaN(parseFloat(value)) && isFinite(value)
Done! Now call your inputs like that:
render(){
const { form } = this.state
return(
<TextField value={form.name.value} onChange={e => this.onChangeHandler('name',e.target.value)} />
)
}
Every time that the input changes the validate function will be triggered, now you have real time form validation, is up to you apply the respectives styles according to the valid prop.
we have to define a validateProperty(input) and use it in our onChange method. here is a basic example how to implement it.first define your state. assuming that i have a form with username and password inputs.
state = { account: { username: "", password: "" }, errors: {} };
validateProperty = (input) => {
if (input.name === "username") {
if (input.value.trim() === "") return "username is required";
}
if ((input.name = "password")) {
if (input.value.trim() === "") return "password is required";
}
};
now we have to use it in on handleChange
e.currentTarget returns input field and i named it as input and did object destructuring
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const account = { ...this.state.account };
account[input.name] = input.value; //currentTarget returns input field
this.setState(() => ({ account, errors }));
};
for each input field I added "name" attribute. so errors[input.name] will be errors[e.currentTarget.name] if name="username" username field, if name="password" password field.
I can't figure out why my input is not updating. Here is my code:
state = {
org: {
orgName: ''
}
};
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
render() {
let { org } = this.state
return (
<input
value={org.orgName}
onChange={this.updateInput('orgName')}
/>
)
}
I type data into the input. It calls updateInput and sets the state. When render is called, the org.orgNameis '' again. This should be working.
I have even added a log in the setState callback:
this.setState({
[field]: event.target.value
}, () => console.log(this.state.org))
and it logs out the org info that has been entered into the input
What am I missing? How do I make this work?
You have a nested object in your state - you are updating this.state.orgName instead of this.state.org.orgName
updateInput = field => event => {
this.setState({
[field]: event.target.value
})
}
needs to be
updateInput = field => event => {
this.setState({
org: {
...this.state.org,
[field]: event.target.value
}
})
}
Would recommend you avoid nesting objects in state though going forward. Will prove difficult to optimize later on.
I'm wrapping my forms to provide automatic validation (I don't want to use redux-form).
I want to pass an onSubmit handler which must be fired after every input in form is validated: but how do I wait for form.valid property to turn into true && after wrapping submit was fired? I'm missing some logic here!
//in my Form.js hoc wrapping the forms
#autobind
submit(event) {
event.preventDefault();
this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
// ==> what should I do here? Here I know submit button was pressed but state is not updated yet with last dispatch result reduced!
//if(this.props.form.valid)
// this.props.submit();
}
render() {
return (
<form name={this.props.formName} onSubmit={this.submit}>
{ this.props.children }
</form>
);
//action.js validating given input
export const syncValidateInput = ({ formName, input, name, value }) => {
let errors = {<computed object with errors>};
return { type: INPUT_ERRORS, formName, input, name, value: errors };
};
//action.js validating every input in the form
export const syncValidateForm = ({ formName, form }) => {
return dispatch => {
for(let name in form.inputs) {
let input = form.inputs[name];
dispatch(syncValidateInput({ formName, input, name: input.name, value: input.value }));
}
};
};
//in my reducer I have
case INPUT_ERRORS:
let errors = value;
let valid = true;
let errorText = '';
_.each(errors, (value, key) => {
if(value) {
valid = false;
errorText = `${errorText}${key}\n`;
}
});
form.inputs[name].errors = errors;
form.inputs[name].valid = valid;
form.inputs[name].errorText = errorText;
_.each(form.inputs, (input) => form.valid = !!(form.valid && input.valid));
return state;
Help!
Depending on your build config you could use Async/Await for your submit function. Something like
async submit(event) {
event.preventDefault();
const actionResponse = await this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
if (actionResponse && this.props.form.valid) { //for example
// finish submission
}
}
And I think you will need to update your syncValidateForm slightly but this should put you on the right path.