I am trying to validate an HTML-Input field rendered with React to only accept numbers. My current attempt to do that.
What must be done in order to validate the Input field to only accept numbers?
phone: {
elementLabel: 'Phone Number',
elementType: 'input',
elementConfig: {
type: "number",
placeholder: 'Phone Number',
name: "phone",
id: "phone",
min: "0",
pattern: "[0-9]*",
},
value: '',
form_value: "",
validation: {},
valid: false,
touched: false,
className: "form-control",
changed: (event) => this.inputChangedHandler(event,
"phone"),
blured: (event) => this.inputBlurHandler(event,
"phone")
},
render(){
<div>
<Input {...this.state.name}/>
</div>
}
inputChangedHandler = (event, inputIdentifier) => {
const updatedForm = {
...this.state
};
const updatedFormElement = {
...this.state[inputIdentifier]
};
Utils.updateElementValue(updatedFormElement, event, inputIdentifier);
updatedFormElement.touched = true;
updatedForm[inputIdentifier] = updatedFormElement;
let formIsValid = true;
for (let inputIdentifier in updatedForm) {
if (updatedForm[inputIdentifier].elementType) {
formIsValid = updatedForm[inputIdentifier].valid && formIsValid;
}
}
this.setState({ [inputIdentifier]: updatedFormElement, formIsValid: formIsValid });
}
Also this is my change handler looks like
Number is not validated when taking input from the control. While using this, I want validaiton that only input numbers, not even any special characters.
Edit 1:
I use the following versions:
bootstrap: ^4.3.1
react: ^16.8.6
I think the pattern you are using is wrong. It should be ^[0-9]*$ which accepts any number of digits, including none.
Related
I have a useState problem, I am trying to add an object in the state but value errors, console.log(errors):
{name: "", description: "", price: "", category: "", image: "Image cannot be empty"}
I want all values in one object.
Hope everyone will help me because I am new.
Here is my code:
const [errors, setErrors] = useState({
name: '',
description: '',
price: '',
category: '',
image: '',
});
const [formIsValid, setFormIsValid] = useState(true);
const handleValidation = () => {
//Name
if(!formState.name){
setFormIsValid(false);
setErrors({
...errors,
name: 'Name cannot be empty',
});
}
//category
if(!formCategory.category){
setFormIsValid(false);
setErrors({
...errors,
category: 'Category cannot be empty',
});
}
//Image
if(!image.image){
setFormIsValid(false);
setErrors({
...errors,
image: 'Image cannot be empty',
});
}
return formIsValid;
};
You are trying to make state update based on your previous state. Basically the issue is following:
you want to make few state updates synchronously (when few properties are not valid) and only last update is applied.
So why this happen?
In the code above when errors is equal to initial state and all fields are empty will happen following
setErrors({
...errors,
name: 'Name cannot be empty',
});
is the same as
setErrors({
description: '',
price: '',
category: '',
image: '',
name: 'Name cannot be empty',
});
after that you are entering another if statement and there you are performing another setState operation with the same state, and errors array gone be the same
so this
setErrors({
...errors,
category: 'Category cannot be empty',
});
will be transformed to this
setErrors({
description: '',
price: '',
category: 'Category cannot be empty',
image: '',
name: '',
});
React will schedule all the setState one after each other and as you are assigning object it will just override the last existing one and name property will be cleared.
So it is two way to solve current issue if you want to use object as a state:
Generate object and then execute setState one time with combined object, that contains all the changes:
const [errors, setErrors] = useState({
name: '',
description: '',
price: '',
category: '',
image: '',
});
const handleValidation = () => {
const newErrorsState = {...errors};
let formIsValid = true;
//Name
if(!formState.name){
formIsValid = false;
newErrorsState.name = 'Name cannot be empty';
}
//category
if(!formCategory.category){
formIsValid = false;
newErrorsState.category = 'Category cannot be empty';
}
//Image
if(!image.image){
formIsValid = false;
newErrorsState.image = 'Image cannot be empty';
}
if (!formIsValid) { // if any field is invalid - then we need to update our state
setFormIsValid(false);
setErrors(newErrorsState);
}
return formIsValid;
};
Second way to solve this issue is to use another way to set your state,
You can use function inside of your setState handler. Inside that function you will receive the latest state as parameter and that state will have latest changes.
More information can be found by the link
const [errors, setErrors] = useState({
name: '',
description: '',
price: '',
category: '',
image: '',
});
const [formIsValid, setFormIsValid] = useState(true);
const handleValidation = () => {
//Name
if(!formState.name){
setFormIsValid(false);
const updateNameFunction = (latestState) => {
return {
...latestState,
name: 'Name cannot be empty',
};
}
setErrors(updateNameFunction);
}
//category
if(!formCategory.category){
setFormIsValid(false);
setErrors((prevErrors) => {
return {
...prevErrors,
category: 'Category cannot be empty',
}
});
}
//Image
if(!image.image){
setFormIsValid(false);
setErrors((prevErrors) => {
return {
...errors,
image: 'Image cannot be empty',
};
});
}
return formIsValid;
};
Not sure what you are trying to achieve.
Anyway I think you want to validate multiple fields and get their errors together.
Please look at react-hook-form documentation
Every field that registered to the form will give you his errors "automatically".
In addition, You can add validations in the object of the second argument of register like that :
<Input {...register("firstName", { required: true })} />
List of available validations that you can add
Hope thats help you :) Good luck!
from where formState is coming? and you can use usereducer or react-hook-form library for better approch
const [checkedHealth, setCheckedHealth] = useState(checkboxHealthLabels);
const handleChangeHealth = (event) => {
setCheckedHealth([
...checkedHealth,
[event.target.name]: event.target.checked,
]);
};
and checkboxHealthLabels file :
export const checkboxHealthLabels = [
{ name: "Alcohol-Free", checked: false },
{ name: "Celery-Free", checked: false },
{ name: "Dairy-Free", checked: false },
];
now I want to change just one object for example : { name: "Alcohol-Free", checked: false },
and other values have to stay same. How can I do that?
Find the index of the object in the array with the same name, then toggle it as needed:
const handleChangeHealth = ({ target }) => {
const { name } = target;
const index = checkedHealth.findIndex(obj => obj.name === name);
setCheckedHealth([
...checkedHealth.slice(0, index),
{ name, checked: target.checked },
...checkedHealth.slice(index + 1)
]);
};
You could also consider having the state be an object (with the names being the object properties) instead of an array, it might be easier.
There is something that I am blocking and it makes no sense at all. For you who are familiar with react-hook-form, I am attempting to create a dynamic field array that renders according to the state object. The thing is, it renders on the first render but not on the second render.
Example:
let subK = [{ name: '' }]
if (kategories[kategori] !== undefined) {
//subK = kategories[kategori].subKategori.map(x => ({ name: JSON.stringify(x) }))
subK = kategories[kategori].subKategori.map(x => ({ name: (x) }))
}
console.log(subK) // it logs[{name: 'kat1'},{name: 'kat2'}]
//defines the form
const { register, control, handleSubmit } = useForm({
defaultValues: {
subKategori: subK
}
});
does not render subK.
But if I do
let subK = [{ name: '' }]
if (kategories[kategori] !== undefined) {
//subK = kategories[kategori].subKategori.map(x => ({ name: JSON.stringify(x) }))
subK = kategories[kategori].subKategori.map(x => ({ name: (x) }))
}
console.log(subK)
//defines the form
const { register, control, handleSubmit } = useForm({
defaultValues: {
subKategori: [{name: 'kat1'},{name: 'kat2'}]
}
});
it renders as it is supposed too.
What am I doing wrong?
There is a minor issue in the structure of subKategori at line number 8. It seems an array is in stringified form. But for map, you need an array. Converting it as following at line number 8 shall work :
....
kat1: {
subKategori: ["kat1", "kat2"]
},
...
Here is the updated sandbox link
I am in a React class-based component and a property on my state like so:
state = {
controls: {
email: {
validation: {
required: true,
isEmail: true
},
invalid: false,
},
password: {
validation: {
required: true,
minLength: 6
},
invalid: true,
},
disabledSubmit: true,
}
};
I have an inputChangedHandler triggered from an input component:
inputChangedHandler = (event, controlName) => {
console.log("[STATE]", this.state.controls);
const updatedControls = {
...this.state.controls,
[controlName]: {
...this.state.controls[controlName],
value: event.target.value,
invalid: !this.checkValidity(
event.target.value,
this.state.controls[controlName].validation
)
},
touched: true
};
console.log("[UPDATEDCONTROLS]", updatedControls);
this.setState({ controls: updatedControls }, () => {
this.disabledSubmitHandler();
});
};
And a disabledSubmitHandler that should be being called from within my inputChangedHandler :
disabledSubmitHandler() {
if (
!this.state.controls["email"].invalid &&
!this.state.controls["password"].invalid
) {
this.setState(
{ disabledSubmit: true },
console.log("[DISABLEDHANDLER] TRUE")
);
}
}
The prop is set on my button component in my JSX like so:
<Button
value="submit"
clicked={this.submitHandler}
disabled={this.state.disabledSubmit}
/>
This does not work, but I'm not sure what's happening?
I think maybe this bit seems to need fixing:
disabledSubmitHandler() {
if (
!this.state.controls["email"].invalid && //if email is not invalid
!this.state.controls["password"].invalid //if password is not invalid
) {
this.setState(
{ disabledSubmit: true },
console.log("[DISABLEDHANDLER] TRUE")
);
}
}
That code says if the email and password are valid, disable the input. I think it should be:
disabledSubmitHandler() {
if (
!this.state.controls["email"].invalid &&
!this.state.controls["password"].invalid
) {
this.setState(
{ disabledSubmit: false },
console.log("[DISABLEDHANDLER] FALSE")
);
}
}
Plas as #an0nym0us mentioned, disabledSubmit is nested inside controls.
Also, on a side note, it seems a little odd that you would call a function which sets state, only to call another function which sets state inside that function, as a callback to set state (inputChangedHandler calling disabledSubmitHandler'). It seems you could call that disabled check from withininputChangedHandlerpassing it yourupdatedControls, and return true/false fordisabledSubmit, resulting in a single call tosetState`
I am wanting to be able to select a language I have called "Skip" more than one time without it disappearing from the dropdown. Currently, any language I select will disappear from the dropdown. Is this possible? Here's my code:
const languages = [
{ key: 'skip', text: 'Skip', value: 'Skip' },
{ key: 'english', text: 'English', value: 'English' },
{ key: 'french', text: 'French', value: 'French' },
]
handleAudioInputChange = (e, { name, value }) => this.setState( { [name]: value })
<Form.Select fluid search label='Audio Channels' name='audiochannelsValue' value={audiochannelsValue} options={languages} placeholder='Choose Audio Channels' onChange={this.handleAudioInputChange} multiple = "true"/>
I've tried multiple things such as hideSelectedOptions = {false}, but this does not seem to work. Any ideas?
If you only want a string based on the user input, you could do:
handleAudioInputChange = (e, { value }) => {
this.setState(prevState => {
const newVal = `${prevState.audiochannelsValue.length ? '/' : ''}${value}`;
return {
textValue: prevState.textValue.concat(newVal),
audiochannelsValue: value
};
});
}
This will build a string based on the previous state and separate each value with /. Haven't tested it, but it should generate (in order):
English
English/Skip
English/Skip/French
English/Skip/French/Skip