how to multiple validation in form with react hook - reactjs

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

Related

The state of usestate is not updated for all error for the forms

How can I update the state for all of the errors?
for example, I have to validate the fields name and last name:
const handleSubmit = () => {
const { name, lastName } = fields
let existError = false
if (!verify(name)) {
existError = true
setError({ ...error, ['name']: 'name is bad' })
}
if (!verify(lastName)) {
existError = true
setError({ ...error, ['lastName']: 'lastname is bad' })
}
if (!existError) {
...
}
}
console.log('ERROR', error)
I can only see this error
ERROR {lastName: 'lastname is bad'}
i am waiting this result:
{name: 'name is bad', lastName: 'lastname is bad'}
The value of error within your handleSubmit function won't update until the next render, so when you set lastName:
setError({ ...error, ['lastName']: 'lastname is bad' })
error still doesn't have the name property and you end up overwriting the name update with the lastName update.
You're effectively doing this:
const error = {
foo: 123
}
let state = {
...error,
name: 'name is bad'
}
console.log(state); // { foo: 123, name: 'name is bad' }
state = {
...error, // still doesn't contain 'name'
lastName: 'last name is bad'
}
console.log(state); // { foo: 123, lastName: 'last name is bad' }
You could avoid this by collecting your errors in a separate object and doing a single state update that includes all of them.
let errors = {};
if(!verify(name)) {
errors.name = 'name is bad'
}
if(!verify(lastName)) {
errors.lastName = 'last name is bad'
}
setError({ ...error, ...errors });
Try this instead:
if(!verify(name)){
setState(prevError => {
return {...prevError, name: 'name is bad'}
});
}
if(!verify(lastName)){
setState(prevError => {
return {...prevError, lastName: 'lastName is bad'}
});
}
The value error wasn't updated after the first if block, due to asynchronous reasons. When you used the error value in the second if block, it still used it's orignal value.

When my page loads I want useEffect to make the call to my server and setFormValues. But formValues stays in its initial state. What am I missing?

This is my first time posting. When I console.log(res.data), the results are:
case_size: "4"
category: "Dairy"
createdAt: "2022-05-27T16:08:43.762Z"
description: "Amish Cntry, unpstr"
item_name: "Milk"
location: "Walk-in"
unit_measure: "1 gal"
updatedAt: "2022-05-27T16:08:43.762Z"
__v: 0
_id: "6290f78bb9acdf9aefadd77b"
But, console.log(formValues) is:
case_size: ""
category: ""
description: ""
item_name: ""
location: ""
unit_measure: ""
EditItem.jsx
const [formValues, setFormValues] = useState({
location: '',
category: '',
item_name: '',
description: '',
unit_measure: '',
case_size: ''
})
useEffect(() => {
let isCancelled = false
const getItem = async () => {
let url = process.env.NODE_ENV === 'local' ? `http://localhost:3001/api/item/${id}` : `https://server-inventory-app.herokuapp.com/api/item/${id}`
const res = await axios.get(url)
if (!isCancelled) {
setFormValues(res.data)
console.log(res.data)
console.log(formValues)
}
}
getItem()
return () => {
isCancelled = true
}
}, [id])
useState hook is async, and will not be changed immediately.
setFormValues(res.data) // not updated
console.log(res.data)
if you want yo see state updating, you can use useEffect hook.
useEffect(() => {
console.log("updated state:",formValues)
},[formValues])

Errors state is not updating on first click

I am making a form to update addresses. When the user clicks on the submit button, the form isn't being submitted but after the click the errors are updated correctly and the form submits fine. Here is what im working with
const [errors, setErrors] = React.useState({
firstName: '',
lastName: '',
streetAddress: '',
streetAddress2: '',
city: '',
zipCode: ''
});
const validateRequiredFields = () => {
let inputError = {};
Object.keys(user).forEach(
(key) => {
inputError = { ...inputError, ...RequiredValidation(user, key) };
}
);
setErrors({ ...errors, ...inputError });
The Validation returns inputError which takes in the values and sets to null instead of empty strings if theres no error. When I console.log this it returns the correct format. Also if there is errors, the errors are updated correctly on first click. Having looked into this I saw that most recent state isn't always fetched which I believe is my problem & the other threads I looked into recommended something like this although I don't know if thats how I am supposed to set it up.
setErrors({ ...errors, ...inputError }, () => {
setErrors({ ...errors, ...inputError });
});
Use
setErrors((prevErrors) => ({ ...prevErrors, ...inputError }));

form.map is not a function error, only appearing after getting data from an input field

I am trying to get data from input fields and add the values to a list to send as a post request.
I have a form object
const [form, setForm] = useState([
{
hole1: 1,
par1: '',
stroke1: '',
yards1: ''
},
{
hole2: 2,
par2: '',
stroke2: '',
yards2: ''
},
//repeat for 18 holes
Then when I click the submit button after filling in the form,
const list = form.map((holes, i) => {
const num = i +1;
return {
hole: num,
par: holes[`par${num}`],
stroke: holes[`stroke${num}`],
yards: holes[`yards${num}`],
course: {
courseid: 3,
},
};
});
If the form is empty this works fine and if I log the list
0: {hole: 1, par: '', stroke: '', yards: '', course: {…}}
1: {hole: 2, par: '', stroke: '', yards: '', course: {…}}
2: {hole: 3, par: '', stroke: '', yards: '', course: {…}}
As soon as I enter some values into the form though, I get the error Uncaught TypeError: form.map is not a function
I understand form needs to be an array to use the map function, but I cannot figure out why it is working when the form is empty, but as soon as I add data it breaks? Is the data type changing when I add data?
edit,
Code for my onChange method which sets the values in the form.
const onChange = (event) => {
const { value, name } = event.target;
setForm((previousForm) => {
return {
...previousForm,
[name]: value
};
});
setErrors((previousErrors) => {
return {
...previousErrors,
[name]: undefined
};
});
};

Unable to validate number in React with Bootstrap

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.

Resources