Errors state is not updating on first click - reactjs

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 }));

Related

How do i clear out form fields inside a form onSubmit

so i have this formData which i want to clear(want to clear the form fields) out when i click on the submit button in the form .
const[formData,setFormData] = useState(
[{name:'',description:'',type:'',required:''}]
)
so in my handleSubmit Function is this the right way to clear it?(below) or is there any other way?
setFormData([{name:'',description:'',type:'',required:''}])
That's fine, you are doing well but with some refactoring it should be:
const initialState = {
name: '',
description: '',
type: '',
required: ''
};
const [formData, setFormData] = useState(initialState)
// later for resetting
setFormData(initialState)

How to use Formik with React-Select with isMulti?

I'm building a system where we can create a project and assign it to several users by filling up a form using Formik, Yup and React-Select.
However, I'm really struggling with passing the array of users when submitting my form using Formik, Formik doesn't receive the data from React-Select.
Here are snippets of the code:
const validationSchema = () => {
return Yup.object().shape({
title: Yup.string().required('A title is required'),
description: Yup.string().required('A description is required'),
assigned: Yup.array().required('At least one assigned user is required'),
});
};
const formik = useFormik({
initialValues: {
title: '',
description: '',
assigned: [],
},
validationSchema,
validateOnBlur: false,
validateOnChange: false,
onSubmit: (data) => {
ProjectService.create(data).then(() => {
navigate('/projects');
window.location.reload();
});
},
});
The component containing the options to select:
Assign to:
<SelectList
name="users"
options={convertToReactSelectObject(users)}
onChange={(assignedUsers) =>
formik.setFieldValue('assigned', assignedUsers.value)
}
value={formik.values.assigned}
/>
{formik.errors.assigned ? formik.errors.assigned : null}
Does someone have an idea of what I should fix to make it work?
Thank you!
My mistake was that I was trying to only pass the "value" field of the array to Formik which doesn't work. So I've passed the whole array of assigned users
<SelectList
options={convertToReactSelectObject(users)}
value={formik.values.assigned}
onChange={(assignedUser) =>
formik.setFieldValue('assigned', assignedUser)
}
/>
Then on the backend, the new project will only take the value field of the assigned users.
// Create a Project
const project = new Project({
title: req.body.title,
description: req.body.description,
assigned: req.body.assigned.map((e) => e.value),
});

Fonctionnement formulaire contrôlé react?

I have a problem with react. with a form, I created more inputs that are controlled. I created a function that runs when the form changes. which but updates a state. but reacte gives me an error.
VM2529 react_devtools_backend.js:4026 Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.
handleChange func:
let DefaultValue = {
firsName: '',
lastname: '',
email: '',
message: ''
}
const [output, setOutput] = useState(DefaultValue)
const handleChange = (e) => {
setOutput({ [e.target.name]: e.target.value });
}
thank you for your reply.
setOutput({ [e.target.name]: e.target.value });
This will not be merged with the old state, it will replace the old state. So if they change lastName, the new state will be { lastName: "some string" }, and all other values will be undefined.
Instead, you need to copy all the other values from the old state:
setOutput(prev => ({
...prev,
[e.target.name]: e.target.value
});

how to multiple validation in form with react hook

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

useFieldArray react-hook-form

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

Resources