How to reset a certains fields after submit in Formik - reactjs

I am using Formik and I need to make this logic.
I am have a big form, and after submission I need to reset certain fields in this form
There is 2 buttton: Save and Save & New
save will save the data and redirect.
Save & New will save the data and reset the first two fields.
I tried to use:
resetForm but it resets all the form
setValues does not work
setFieldValues prevents the api request
set values manually also does not work
Here is the code
const initialValues = { value1: "", value2: "", value3: "", value4: "" };
<Formik
initialValues={initialValues}
enableReinitialize
validationSchema={Schema}
validateOnChange={false}
validateOnBlur={false}
onSubmit={(values, { setSubmitting, resetForm }) => {
props.addData(values); // api request
setSubmitting(false);
// resetForm(initialValues); // this will reset all the form fields
// values.value1 = ""; // does not work
}}
>
{({ values, handleChange, handleSubmit, setFieldValue, errors }) => (
<Form>
<Row>
<Field type="text" name="value1" />
<Field type="text" name="value2" />
<Field type="text" name="value3" />
<Field type="text" name="value4" />
</Row>
<StyledRow>
<Button
variant="outline-primary"
onClick={() => {
handleSubmit();
history.push("/tableOverview");
}}
>
Save
</Button>
<Button
variant="outline-primary"
onClick={() => {
handleSubmit();
// this prevents the api request, because of async code I guess
// setFieldValue("value1", "");
// setFieldValue("value2", "");
}
>
Save & New
</Button>
</StyledRow>
</Form>
)}
</Formik>

On submitting the form, you can set whatever the values you dont want to peresist using setFieldValue and later setSubmitting to false, So that the values you want will be persisted and you can submit the form again
Please find the example codesandbox here

Related

Handling routes after submitting form

Hello my great teachers of StackOverflow. I'm going through ben awads fullstack tutorial and am trying to add an image upload feature to create post. Looks like everything works well, inserts posts (including image) into db. However, after submitting my form, it wont send me to the home page (stays on the same page with current values inserted). It is set so that if there are no errors, route me to the homepage. Im assumming i have no errors cause the post inserts into database. Any help will be greatly appreciated.
const CreatePost: React.FC<{}> = ({}) => {
const router = useRouter();
const [createPost] = useCreatePostMutation();
return (
<Layout>
<Formik
initialValues={{ title: "", text: "", file: null }}
onSubmit={async (values) => {
console.log(values);
const { errors } = await createPost({
variables: values,
});
if (!errors) {
router.push("/");
}
}}
>
{({ isSubmitting, setFieldValue }) => (
<Form>
<InputField name="title" placeholder="title" label="Title" />
<Box mt={4}>
<InputField name="text" placeholder="text..." label="Body" />
</Box>
<Input
mt={4}
required
type="file"
name="file"
id="file"
onChange={(event) => {
setFieldValue("file", event.currentTarget.files[0]);
}}
/>
<Button mt={5} type="submit" isLoading={isSubmitting}>
create post
</Button>
</Form>
)}
</Formik>
</Layout>
);
};
You can use router.push hook.
You need to pass validate function as props to Fomik to prevent submitting only with 2 fields. I have added a basic validate object. But you can use Yup package also. Formik will return an error object inside the form and you need to make sure form does not get submitted in error state. For that get the errors object and as you are using custom Button component pass true/false by checking if any errors exist. I added the code.
You can get more details here.
router.push("/home");
.....
const validate={values => {
const errors = {};
if (!values.title) {
errors.email = 'Required';
}
if (!values.text) {
errors.text= 'Required';
}
if (!values.file) {
errors.file= 'Required';
}
return errors;
}}
<Formik
initialValues={{ title: "", text: "", file: null }}
validate
onSubmit={async (values) => {
.......
{({ isSubmitting, setFieldValue, errors }) => (
..........
<Button mt={5} type="submit" isLoading={isSubmitting} isFormValid={!Object.values(errors).find(e => e)}>
create post
</Button>

Formik resetting form to initial values

I am using Formik and have the following setup below where I want to be able to reset the form when the user presses the "Cancel" button. On return to the form, all form values should be reset to initialValues which are all nulls.
<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={handleSubmit}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, resetForm }) => (
<Form>
.....
I have the following code for the Cancel button:
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
{resetForm}
setMenu("Home")
}}
/>
After entering some text into a form field and pressing the Cancel button, which directs me back to the Home page, I then go back to the form and notice that my text is still in state within the form and not resetting.
Can anyone please assist with what I am missing.
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
resetForm()
setMenu("Home")
}}
/>
You should use the resetForm() as a function call
you just need to set values of the input boxes to formik values:
<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={handleSubmit}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, resetForm }) => (
<input value={values.propertyName}/>
<Form>
and now resetForm should work well
You must change values through values since you dont have access to resetForm from the button.
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
to={'/home'}
onClick={() => {
values = {
someProperty: null,
}
}}
/>
As as I see your are using Material UI. I notice that you have a "to" property in your Button component I think you have to decide either remaining on the same page and reset your form or redirecting to another page. If you want to remain on the same page I would suggest you to get rid of it because this causes some conflict. You can implement it like this:
return (<Formik
enableReinitialize
initialValues={{
...INITIAL_FORM_STATE
}}
validationSchema={ FORM_VALIDATION }
onSubmit={(values, actions) => {
actions.setSubmitting(false);
console.log("Submit form", values);
}}
>
{({ values, errors, isSubmitting, isValid, setFieldValue, handleChange, handleSubmit, resetForm }) => (
<Form onSubmit={handleSubmit}>
..... some inputs
<Button
text="Cancel"
startIcon={<UndoIcon />}
variant="contained"
color="default"
className={classes.buttons}
component={Link}
onClick={() => handleReset(resetForm)}
/>
</Form>
)}
</Formik>
);
And inside you class create a handleReset method:
const handleReset = (resetForm) => {
if (window.confirm('Reset?')) {
resetForm();
setMenu("Home");
}
};
`
const myForm = useFormik({
initialValues:{
value1:'',
value2:''
},
onSubmit:( values ) = >{
//submit data
........
//reset form after submit
myForm.resetForm()
}
)
on return
<form onSubmit={myForm.submit}>
........
<Button type="submit"/>
<Button onClick={()=>myForm.resetForm()}>Reset</Button>
</form>
`

Formik validate initial values on page load

Code below validate when submit and onFocusOut of textbox fine, What I expect it to trigger validation first reload of the page with initial values.
Tried validateOnMount, as well as others but not worked.
Whats missing here?
const RoleValidationSchema = Yup.object().shape({
Adi: Yup.string()
.min(2, "En az 2 karakter olmalıdır")
.max(30, "En fazla 30 karakter olmalıdır")
.required("Gerekli!")
})
const Role = (props) => {
return (
<div>
<Formik
onSubmit={(values, { validate }) => {
debugger
validate(values);
alert("Submietted!")
props.handleFormSubmit()
}}
initialValues={{
Adi: "d"
}}
validationSchema={RoleValidationSchema}
validateOnMount={true}
validateOnChange={true}
validateOnBlur={true}
render={({ errors, touched, setFieldValue, ...rest }) => {
debugger
return (
<Form>
<Row>
<Col sm="12">
<Label for="Adi">Rol Adi</Label>
<FormGroup className="position-relative">
<Field
autoComplete="off"
id="Adi"
className="form-control"
name="Adi"
type="text"
/>
<ErrorMessage name="Adi">
{(msg) => (
<div className="field-error text-danger">{msg}</div>
)}
</ErrorMessage>
</FormGroup>
</Col>
<Tree dataSource={treeData} targetKeys={targetKeys} expandKeys={[]} onChange={onChange} />
</Row>
<button type="submit" className="btn btn-success" > Kaydet</button>
</Form>
)
}}
/>
</div>
)
}
This worked for me to show validation error on form load.
useEffect(() => {
if (formRef.current) {
formRef.current.validateForm()
}
}, [])
Try to add enableReinitialize Prop to your Formik Component
<Formik
enableReinitialize
......
/>
I had a bit specific case with dynamic Yup schema. Schema was generated based on API data. And validateOnMount was failing as when component was mounted there was no schema yet (API response not received yet).
I end up with something like this.
Inside component I used useEffect. Notice: I used useRef to be able to reference Formik outside of form.
useEffect(() => {
if (formRef.current) {
// check that schema was generated based on API response
if(Object.keys(dynamicValidationSchema).length != 0){
formRef.current.validateForm()
}
}
}, [initialData, dynamicValidationSchema])
Formik:
<Formik
innerRef={formRef}
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={props.handleSubmit}
>
It's possible to provide a initial value for validation in the "initialIsValid" prop.
const validationSchema= Yup.object()
const initialValues = { ... }
const initialIsValid = schema.isValidSync(initialValues)
return <Formik
initialValues={initialValues}
validationSchema={validationSchema}
initialIsValid={initialIsValid }
...
>
...
</Formik>
The only solution I found is to explicitly add initialErrors={{..}} to Formik.
In the below example, it's added depending on some condition. Unfortunately that duplicates the Yup validation/message which I also have in the schema. Nothing else worked, no validateOnMount or anything else.
<Formik
enableReinitialize
validationSchema={schema}
onSubmit={ (values) => {
submitForm(values);
}}
initialValues={initialFormValues}
/* Unfortunately a duplication of Yup Schema validation on this field,
but can't force Formik/Yup to validate initially */
initialErrors={!isEmptyObject(initialFormValues) &&
initialFormValues.inactiveApproverCondition &&
{'approverId': 'Selected Approver is no longer eligible. Please choose a different Approver to continue.'}}
>
You should try updating your code follow this
render={({ errors, touched, setFieldValue, isValid, ...rest })
and
<button type="submit" className="btn btn-success" disabled={!isValid}> Kaydet</button>

How to add a Clear Button on a Formik Textfield

I want to add a Clear Button as a convenience to users:
constructor(props) {
this.emailInput = React.createRef();
}
<Field label="Email" name="email" ref={this.emailInput} onChange={onChange} component={TextField}/>
But get this:
Warning: Function components cannot be given refs. Attempts to access this ref will fail.
To reset particular fields, use setFieldValue to set value to an empty string.
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void
Set the value of a field imperatively. field should match the key of values you wish to update. Useful for creating custom input change handlers.
- Formik Documentation
Eg:
<Formik
initialValues={initialValues}
...
>
{({ setFieldValue }) =>
...
<button type="reset" onClick={() => setFieldValue('fieldName', '')}>
Reset This
</button>
...
To reset all fields, use resetForm.
resetForm: (nextValues?: Values) => void
Imperatively reset the form. This will clear errors and touched, set isSubmitting to false, isValidating to false, and rerun mapPropsToValues with the current WrappedComponent's props or what's passed as an argument.
- Formik Documentation
Eg:
<Formik
initialValues={initialValues}
...
>
{({ resetForm }) =>
...
<button type="reset" onClick={resetForm}>
Reset All
</button>
...
Codesandbox : https://codesandbox.io/s/7122xmovnq
Formik has a built in method called resetForm which can be accessed like the other formik methods. In your form you probably have something like
<Formik
initialValues={something}
validationSchem={someSchema}
render={() => {
...some form stuff
}
}
/>
you can access the formik props inside that render method and do what you want with them:
<Formik
initialValues={something}
validationSchem={someSchema}
render={(props) => {
...some form stuff
<button type="button" onClick={() => props.resetForm()}>reset form</button>
}
}
/>
you can try to set a reset button in your form, e.g
<form>
<Field label="Email" name="email" onChange={onChange} component={TextField}/>
<input type="reset" value="Reset">
</form>
I took the example here, it has to reset all of the inputs in the form

How to reset / empty form after submit in Formik

So I've got my form. And I simply want it to be empty after the submit is successfull.
I've seen that I should be using enableReinitializing and change the value manually like : this.values.content = "".
But I'm not managing to understand where can I put this option?
<Formik
enableReinitializing //This is not working
initialValues={{
content: "",
}}
validationSchema={validAddMessageToProjectSchema(
this.props.intl.locale
)}
validateOnBlur={true}
onSubmit={ async ( values: AddMessageToProjectFormValue,
{ setSubmitting }: any
) => { await mutate({ variables: values });
setSubmitting(false);
}}
>
{({ isSubmitting, values, errors, touched, setFieldValue }) => {
return (
<Form className="addMessageToProject-form">
<div>
<FormattedMessage
id="addMessageToProject.descriptionField"
defaultMessage="Describe your post"
>
{msg => (
<Field
name="content"
placeholder={msg}
component={
TextAreaField
}
/>
)}
</FormattedMessage>
</div>
<Button
type="primary"
htmlType="submit"
className="addMessageToProject-form__submit form-submit"
disabled={isSubmitting}
>
<FormattedMessage
id="addMessageToProject.button"
defaultMessage="Send Message"
/>
</Button>
</Form>
);
}}
</Formik>
You can do it like this in onSubmit callback
onSubmit={(values, {setSubmitting, resetForm}) => {
handleSave(params, () => {
resetForm(initialValues)
})
setSubmitting(false);
}}
And this is not enableReinitializing instead use enableReinitialize

Resources