Formik is Not Rendering Initial Values - javascript

I am using Formik for form submission in my React Project.
const initialValues =
submittedAssignment !== undefined
? {
comments: submittedAssignment?.comments,
mk1: mKeys[0],
mk2: mKeys[1],
mk3: mKeys[2],
mk4: mKeys[3],
mk1Num: submittedAssignment?.mk1Num,
mk2Num: submittedAssignment?.mk2Num,
mk3Num: submittedAssignment?.mk3Num,
mk4Num: submittedAssignment?.mk4Num,
}
: {
comments: "",
mk1: "",
mk2: "",
mk3: "",
mk4: "",
mk1Num: 0,
mk2Num: 0,
mk3Num: 0,
mk4Num: 0,
};
console.log(initialValues);
return(
<Formik
initialValues={initialValues}
validationSchema={Yup.object({
comments: Yup.string().required(),
mk1Num: Yup.number().positive().required().max(5),
mk2Num: Yup.number().positive().required().max(5),
mk3Num: Yup.number().positive().required().max(5),
mk4Num: Yup.number().positive().required().max(5),
})}
onSubmit={async (values, { setErrors, setSubmitting }) => {
try {
values = testFunction(values);
console.log(values);
const totalGrade =
values.mk1Num + values.mk2Num + values.mk3Num + values.mk4Num;
const response = await updateSubmittedAssignment(
courseId,
assignmentId,
values,
totalGrade,
studentId
);
if (response.data.data === false)
setErrors({ err: "Error Submitting. Please contact tech" });
else {
setSubmitting(false);
history.push(
`/submittedAssignments/${courseId}/${assignmentId}`
);
}
} catch (error) {
setErrors({ err: error.message });
}
}}
>
{({ isSubmitting, submitForm, isValid, errors }) => (
<Form className="ui form">
<TextArea
name="comments"
placeholder="Enter Comments Here"
rows={10}
/>
<Select
name="mk"
placeholder="Select Marking Key"
options={markingKeys}
onChange={handleMk}
/>
<br />
{mKeys.length > 0 && (
<Grid>
<Grid.Column width={14}>
<TextInput name="mk1" id={1} value={mKeys[0]} />
<TextInput name="mk2" id={2} value={mKeys[1]} />
<TextInput name="mk3" id={3} value={mKeys[2]} />
<TextInput name="mk4" id={4} value={mKeys[3]} />
</Grid.Column>
<Grid.Column width={2}>
<TextInput name="mk1Num" type="number" />
<TextInput name="mk2Num" type="number" />
<TextInput name="mk3Num" type="number" />
<TextInput name="mk4Num" type="number" />
</Grid.Column>
</Grid>
)}
{errors.err && (
<Label
basic
color="red"
style={{ marginBottom: 10 }}
content={errors.err}
/>
)}
<br />
<Button
loading={isSubmitting}
disabled={!isValid || isSubmitting}
type="submit"
fluidsize="large"
color="teal"
content="Submit"
onClick={submitForm}
/>
</Form>
)}
</Formik>
)
Till yesterday, setting the name property in the TextArea and TextInput, the same as the field in the initialValues object was enough to display the initital values. But i dont know what happened, the initial values are not showing up, although when i log them, they have correct values.
Any help is appreciated. Thanks

Try adding enableReinitialize to Formik.
<Formik
enableReinitialize
...
You may have a case where the initialValues values get updated only after the first render.
On the first render Formik takes the initialValues as they are, so if the values didn't get updated yet you will not see them in the form fields.
SOURCE:
https://formik.org/docs/api/formik#enablereinitialize-boolean

Related

React Formik and Checkbox

I need to map dynamicaly on a array. This array can change with a search field and i need to know what checkbox are checked to send my data with a submit but i dont find the right way to do to check the checkbox with Formik. This is my code :
const initialValues = {
checked: [],
};
const onSubmit = (values) => {
console.log(values);
};
<Formik onSubmit={onSubmit} initialValues={initialValues}>
{({ values, handleSubmit }) => (
<form
onSubmit={handleSubmit}
noValidate={true}
>
{Listing.map((Search, index) => {
if (
Search.installationAddress.includes(
searchMeter
) ||
Search.deviceId.includes(searchMeter)
) {
return (
<div
key={index}
>
<Checkbox
color="text.standard"
label={`${Search.installationAddress}`+" Compteur N° " +`${Search.deviceId}`}
type="checkbox"
name="checked"
value={{
contractAccountId:
Search.contractAccountId,
deviceId: Search.deviceId,
}}
size="large"
/>
</div>
);
}
})}
<Checkbox
color="text.standard"
label="I confirm"
onChange={() => setValidationButton(!validationButton)}
value=""
checked={validationButton}
/>
<Button
type="submit"
disabled={!validationButton}
>
ACTIVATE
</Button>
</form>
)}
</Formik>

Formik - Show ErrorMessage on Blur

On the code below there is a text input that shows an error message when:
type invalid email (or leave blank) and hit enter
type invalid email (or leave blank) and blur (or clicking outside)
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange }) => (
<input
type="text"
value={value}
onChange={(e) => handleChange(e.target.value)}
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
type="text"
name="email"
onChange={({ target: { value } }) => {
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};
Now I want to keep the same behavior when doing the following change:
from
<Field
type="text"
name="email"
onChange={({target:{value}}) => {
console.log(value);
setFieldValue("email", value);
}}
/>
to:
<Field
component={TextInput}
name="email"
handleChange={(value) => {
console.log(value);
setFieldValue("email", value);
}}
/>
I mean, basically I'm changing from: type="text" to component={TextInput} which is basically a text input as you can see above.
After doing that change the error happens when:
[SUCCESS] type invalid email (or leave blank) and hit enter
but not when:
[FAIL] type invalid email (or leave blank) and blur (or clicking outside)
Do you know how can I get the error displayed on the second situation?
I'm looking for a standard way to do it and not tricky workarounds.
Here you have a StackBlitz you can play with and if you want you can post your forked one.
Thanks!
The way I handle this is to pass name and id between the component and wrapper, otherwise, Formik has no relationship and therefore no knowledge of what to error.
Here's a fork
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange, name, id }) => (
<Field
id={id} // <- adding id
name={name} // <- adding name
type="text"
value={value}
onChange={handleChange} // You don't need to capture the event, you've already done the work in the wrapper
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
component={TextInput}
id="email" // <- id
name="email" // <- name
handleChange={e => {
const value = e.target.value;
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};

get field value from Formik & Material UI form

I am trying to disable a checkbox group based on the value of a radio group. I followed the method used in the last part of the Formik tutorial. Using react context removes a lot of clutter from the form itself but I'm not sure how to expose some values now.
In the form below, in the CheckboxGroup component, I'm attempting to print the word disabled as an attribute of checkbox1 if radio4's value is "yes". I'm not sure what value should be used here as fields doesn't work. How do I pass a value to the form given the React Context method used?
The form:
export default function HealthAssessmentForm() {
return (
<Formik
initialValues={{
radio4: '',
symptoms: '',
}}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
console.log(JSON.stringify(values, null, 2));
}}
validator={() => ({})}
>
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
{(fields.radio4.value === "yes") ? "disabled" : null}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/>
<button type="submit">Submit</button>
</Form>
</Formik>
)
}
I'm not sure the custom components are relevant here but...
const RadioInputGroup = (props) => {
const [field, meta] = useField({...props, type:'radio'});
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<RadioGroup aria-label={props.name} name={props.name} value={props.value}>
<FieldArray name="options">
{({ insert, remove, push }) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel key={index} {...props} value={option.toLowerCase()} control={<Radio />} label={option} />
))
)}
</FieldArray>
</RadioGroup>
</FormControl>
)
};
const CheckboxGroup = (props) => {
const [field, meta] = useField({...props, type: 'checkbox', });
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<FormGroup>
<FieldArray name="options">
{({ insert, remove, push}) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel
{...field} {...props}
key={index}
control={<Checkbox />}
label={option.label}
/>
))
)}
</FieldArray>
</FormGroup>
<FormHelperText>Be careful</FormHelperText>
</FormControl>
)
}
I wrapped the whole <Form> in a function that passes props as an argument. I then get access to props.values.radio1. However, that has exposed that radio1 does not have a value even when it is clicked, which should be a separate issue.
{(props) => (
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
disabled={props.values.radio1 === "No"}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/> </Form>
)}

Formik FieldArray nested object validations with Yup

https://codesandbox.io/s/wonderful-brattain-928gd
Above, I have added some sample code of the problem I am trying to figure out. I am not sure how to map the errors to the correct items in the FieldArray.
In the example, there are yes/no radio buttons which allow a user to indicate whether they have foods they want to add. If they select 'yes', the food options appear and they must select at least 1 of the foods and enter its expiration date to fully validate.
I am trying to add an "expiration" validation error when the users fails to enter an expiration date in the text field. For example, if I select "Beef" and do not enter an expiration date, the errors populate in the Formik errors. However, I don't know how to map that error to the correct expiration text box.
Any help is appreciated!
Note:
Validations are only triggered when the validated button is clicked
There is codesandbox and code shown belown:
<Form>
<pre
style={{
textAlign: "left"
}}
>
<h3>Data</h3>
{JSON.stringify(values, null, 2)}
<h3>Errors</h3>
{JSON.stringify(errors, null, 2)}
</pre>
<Field
name="food.hasFood"
value
type="radio"
onChange={e => {
setFieldValue("food.hasFood", true);
}}
/>{" "}
Yes
<Field
name="food.hasFood"
value={false}
type="radio"
onChange={e => {
setFieldValue("food.hasFood", false);
}}
/>{" "}
No
{values.food.hasFood && (
<FieldArray name="food.selected">
{arrayHelpers => {
return foodTypes.map(item => (
<div key={item}>
<Field
name={item}
value={item}
type="checkbox"
as={Checkbox}
checked={values.food.selected
.map(f => f.name)
.includes(item)}
onChange={e => {
if (e.target.checked) {
arrayHelpers.push({
name: e.target.value,
expiration: ""
});
} else {
const index = values.food.selected
.map(f => f.name)
.indexOf(e.target.value);
arrayHelpers.remove(index);
}
}}
/>
{item}
{errors.food && touched.food && (
<p>
{Array.isArray(errors.food.selected)
? ""
: errors.food.selected}
</p>
)}
{values.food.selected.map((selectedFood, index) => {
if (item === selectedFood.name) {
return (
<div>
<Field
key={index}
as={TextField}
name={`food.selected[${index}].expiration`}
/>
{console.log(errors)}
{errors.food && touched.food && (
<p>
{Array.isArray(errors.food.selected)
? errors.food.selected[index].expiration
: errors.food.selected}
</p>
)}
</div>
);
}
return null;
})}
</div>
));
}}
</FieldArray>
)}
<button
type="button"
onClick={() => {
validateForm();
}}
>
Validate
</button>
</Form>

Why isn't Formik.setStatus updating the Status state in my form?

I'm using Formik to create a generic contact form in react. I am getting data from my api and attempting to call Formik's setStatus to generate a message to show that the form has been submitted successfully.
For whatever reason the Status state never gets updated to reflect what I put in setStatus.
Here's my code:
import { Formik, Form, useField } from "formik";
import * as Yup from "yup";
import axios from "axios";
import Button from "components/shared/button";
import "styles/contact/form.scss";
const handleSubmit = (values, actions) => {
axios.post("http://localhost:5000/sendemail/", values).then(res => {
actions.setSubmitting(false);
actions.setStatus = {
message: res.data.message
};
setTimeout(() => {
actions.resetForm();
}, 3000);
});
};
const FormField = ({ label, tagName, ...props }) => {
const [field, meta] = useField(props);
const errorClass = meta.touched && meta.error ? "error" : "";
const TagName = tagName;
return (
<>
<label htmlFor={props.id || props.name}>
{label}
{meta.touched && meta.error ? (
<span className="error">{meta.error}</span>
) : null}
</label>
<TagName
className={`form-control ${errorClass}`}
{...field}
{...props}
/>
</>
);
};
const ContactForm = () => (
<Formik
initialValues={{ name: "", email: "", msg: "" }}
validationSchema={Yup.object({
name: Yup.string().required("Required"),
email: Yup.string()
.email("Invalid email address")
.required("Required"),
msg: Yup.string().required("Required")
})}
onSubmit={handleSubmit}>
{({ isSubmitting, status }) => (
<Form className="contact-form">
<div className="row form-group">
<div className="col">
<FormField
label="Name"
name="name"
type="text"
tagName="input"
/>
</div>
<div className="col">
<FormField
label="Email"
name="email"
type="text"
tagName="input"
/>
</div>
</div>
<div className="form-group">
<FormField label="Message" name="msg" tagName="textarea" />
</div>
{status && status.message && (
<div className="message">{status.message}</div>
)}
<Button
id="formSubmit"
text="Send Message"
type="submit"
isSubmitting={isSubmitting}
/>
</Form>
)}
</Formik>
);
export default ContactForm;
Just before my submit button, it should show the <div class="message">Success message</div> after submitting the form. When I try to debug the value of Status is always "undefined".
Any one have a clue what I'm doing wrong here?
The reason it wasn't working is because I tried to set the value of setStatus equal to an object. What I should have done was used it as a method and pass the object as a parameter.
Like so:
actions.setStatus({message: res.data.message});
I feel silly for missing this simple mistake for so long.

Categories

Resources