React-final-form: Reset form after form submission succeed

Created on 3 Dec 2017  路  48Comments  路  Source: final-form/react-final-form

Hi there !
I was wondering how i can reset my form on successful submit ?
Seems that we don't have access to the reset() function in onSubmit().
How can i handle that ?
Thanks for the work by the way , react-final-form is awesome :)

Most helpful comment

Final-form introduced new type in 4.20 version
https://final-form.org/docs/final-form/types/FormApi

Quote from docs:
"restart
() => void
Resets all form and field state. Same as calling reset() on the form and resetFieldState() for each field. Form should be just as it was when it was first created."

Example code:
const onSumbit = form => setTimeout(() => form.restart())
or with destructuring
const onSumbit = { restart } => setTimeout(() => restart())

All 48 comments

Not sure if the best way to proceed with this but you can set a ref on the form instance and on your onSubmit callback fn access the forms reset prop as such

onSubmit = values => {
    this.refs["user-form"].form.reset()
};

Once the fix in #32 is released, the answer is:

<Form
  onSubmit={onSubmit}
  initialValues={{ employed: true }}
  render={({ handleSubmit, reset }) => (
    <form
      onSubmit={event => {
        handleSubmit(event).then(() => {
          reset() // or could be passed directly to then()
        })
      }}
    >
    ...
    </form>
  }/>

Fix published in v1.1.1.

This is still not working for me on latest version.

<Form
    onSubmit={values => {
      return new Promise((res, rej) => {
        axios.post('/api/forms/creditdebit', values)
          .then(response => res())
          .catch(error => res({ error }));
      })
    }}
    validate={validate}
    component={CreditDebitForm}
/>
<Form onSubmit={event => {
      handleSubmit(event).then(() => {
        reset()
      })
    }}>

I get: TypeError: Cannot read property 'then' of undefined. Tried using async too with no luck. It submits, so it is calling.

Hi @erikras thanks for the fix i will try on my side and upgrade to v1.1.1

Hi @davidhenley i managed to make it work see this example :

https://stackblitz.com/edit/react-final-form-reset-submit?file=index.js

Try it using component. It still doesn't work for me. This is my one pain with RFF right now. I'll try again tonight.

Spoke too soon. Still not working. Cannot read property 'then' of undefined. @erikras

const onSubmit = (values, user) => {
  return new Promise(resolve => {
    axios.post('/api/forms/samples', { values })
      .then(response => resolve())
      .catch(error => resolve({ error }));
  });
}

const FormContainer = ({ user }) => {
  return (
    <Form
      onSubmit={(values) => onSubmit(values, user)}
      mutators={{
        ...arrayMutators
      }}
      initialValues={{ products: [{ placeholder: null }] }}
      validate={validate}
      component={SamplesForm}
    />
  );
}
<form onSubmit={event => {
      handleSubmit(event).then(() => reset())
    }}>

Here's a working sandbox with my suggestion from above.

Edit 馃弫 React Final Form - Reset After Submit

Heads up, you cannot use handleSubmit(e).then(... unless you disable your submit on invalid. The middleman does not return a promise so there is no .then

@erikras wouldn't be best to pass the reset or maybe others helpers to onSubmit.

the signature would be like :
onSubmit: (values: Object, callback: ?(errors: ?Object) => void, reset: Function, ....) => ?Object | Promise<?

@stunaz I completely agree. Especially we already have callback, which de facto is just setSubmitError action, so why not to add other actions, like reset.

@erikras the problem with your promise solution is, that this is not always possible. For instance, when you use redux-saga instead of redux-thunk, you wont have promise at your disposal, so it is necessary to have a reset in onSubmit itself, so it could be passed to saga there for a later execution.

opened an issue #69 to get more visibility

EDIT: solved by just putting form.reset() in my handleSubmit method

I have form where I am not accessing event at all and I have problem resetting form after it has been submitted.

I did try this, but form gets reset before it is submitted. As for validation - errors show up, but after that form resets anyway.

onSubmit={() => {
           handleSubmit(values).then(reset);
         }}

I also tried other aforementioned options, but without any success.

Here is the relevant code from my form :
```
handleSubmit(values) {
console.log(values.firstName);

//Setting up values for fetch body
let firstName = values.firstName;
let email = values.email;
let subject = values.subject;
let message = values.message;

fetch('/contact/send', {
method:'POST',
headers:{
'Accept':'application/json, text/plain, /',
'Content-Type':'application/json'
},
body:JSON.stringify({
firstName:firstName,
email:email,
subject:subject,
message:message,
}),
})
.then(res=>res.json())
.then(console.log('here'))
.then(data=>console.log(data))
.catch(err=>console.log(err));
};

render() {
return (



onSubmit={this.handleSubmit}
validate={values => {
const errors = {};
if (!values.firstName) {
errors.firstName = "Required";
}
if (!values.subject) {
errors.subject = "Required";
}
if (!values.email) {
errors.email = "Required";
} else if(!values.email.match( /^([a-zA-Z0-9_.-])+\@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/)) {
errors.email = "Please enter a valid e-mail adress"
}
if (!values.message) {
errors.message = "Required";
}
return errors;
}}
render={({ handleSubmit, submitting, values, reset}) => (
onSubmit={handleSubmit}
>

{({ input, meta }) => (

type="text"
placeholder="Name"
/>
{meta.error && meta.touched && {meta.error}}

)}

....REST OF THE FIELDS
```

"Uncaught TypeError: Cannot read property 'then' of undefined at onSubmit"
:/

Make sure you add return in front of fetch/axios or whatever library you're using to make POST request.

return fetch('/', { method: 'POST'......

Solved putting form.reset() in the parent component

any examples showing a submit success message to the user by rendering a component rather that an alert window or would that require make a class out of the form

As for using reset in onChange() of something inside the form? Anyone have an idea. Please suggest me

@ronn

onSubmit={(event) => {
  const promise = handleSubmit(event);
  console.log('heyyy :-D', promise);
  promise && promise.then(() => {
    reset();
  })
  return promise;
}}

Works perfectly with async/await

onSubmit={async event => {
              await handleSubmit(event)
              form.reset()
            }}

I tried various suggestions to reset the form on successful submission but to no avail. I was just about to give up on Final Form and move back to Formik.

But then I tried my luck with async/await and it works like a charm!
Thanks Final Form :1st_place_medal:

@Aeon-Avinash this still doesn't work for me :(

@NYCJacob Make sure your code looks something like this (following @Aeon-Avinash's example). It's working for me:

    <Form
      onSubmit={values => {
        console.log("values, ", values)
      }}
      render={({ handleSubmit, form }) => (
        <form
          onSubmit={async event => {
            await handleSubmit(event)
            form.reset()
          }}
        >

@hdrdavies I get the form to reset, but that just clears the form. I would like to display some kind of success/failure message. I am afraid to try converting the form to a class and use state because final-form is doing so much internally. I was looking at the response object on return but I still need state I think to conditionally render a message.

when i use

 <Form
      onSubmit={values => {
        console.log("values, ", values)
      }}
      render={({ handleSubmit, form }) => (
        <form
          onSubmit={async event => {
            await handleSubmit(event)
            form.reset()
          }}
        >

because of that

form.reset()

my errors for validation are not shown.

We really could use a handler to clear the contents of the form but does not affect form states like submitSucceeded which are often used to display a success message.

Regarding @golestanirad post on validation errors not showing - I had the same problem and it took me a while to find the solution. I think it's not because of form.reset(). In my case, when I had validation errors, async handleSubmit was not executed at all and returned undefined, then form reset was executed. What worked for me was checking if handleSubmit is undefined or returns empty object before resetting the form.

@golestanirad you don't have a form variable so you're calling reset() on undefined variable, that must be your issue.
If your destructure form in render property, then also extract reset function and call it directly:
https://github.com/final-form/react-final-form/issues/21#issuecomment-349024729

Funcionou dessa forma
const onSumbit = (value,form) => { console.log(values) setTimeout(form.reset) }

Not sure if i am doing something wrong.

However - form.reset used as described above indeed resets its values but does not reset field state. Which results in validation errors after the submission.

So what I have to do is to reset each individual field state manually like:

              handleSubmit(ev)?.then(() => {
                form.reset();
                form.resetFieldState("amount");
                form.resetFieldState("ingredient");
              });

So either I am missing some prop (which I don't seem to see in docs) which would reset not only values but also whole field state or form.reset does not work as one would expect.

@tomkis

  1. you code will only reset if you handleSubmit is async or excplicitly return a promise (to execute the "?.then" callback)
  2. What part of field state are you talking about ?
    because once the reset is done your validation function get the initialValues, which can be valid or not depending on your validation function

ad 1) yes I am well aware of that

ad 2) - i am talking about touched flag which remains true even after resetting the form - only resetFieldsState resets it back.

I'm seeing the same issue when I tried (here https://codesandbox.io/s/epic-platform-wtm5s)

Maybe you could use "dirtySinceLastSubmit" instead ?

I'm not sure this code is the best way but this is working for me:

import React from 'react';
import { Form } from 'react-final-form';

const Component = () => {
  let handleResetForm = null;

  const handleSubmitForm = () => {
    // Do something...
    handleResetForm();
  };

  return (
    <Form
      onSubmit={handleSubmitForm}
      render={({ handleSubmit, pristine }) => {

        // Referenced the reset method to out of the form
        if (!handleResetForm && form && form.reset) {
      handleResetForm = form.reset;
        }

        return  <form onSubmit={handleSubmit}> ... </form>;
      }}
    />
  );
}

"final-form": "^4.18.5"
"react-final-form": "^6.3.0"

@soroushchehresa quick notes:

  • you are reassigning a const variable.
  • you should add form in the render prop function

This should be enough:

<form onSubmit={handleSubmit.then(form.reset)}>

@VincentCharpentier Thanks for the notes.
In my case, the handleSubmit method doesn't return a promise!

Ah ok. Then I would suggest something like this:

<form 
  onSubmit={
    async (...args) => {
      await handleSubmit(...args);
      form.reset();
    }
  }>

@VincentCharpentier My handleSubmit method doesn't return a promise,
So I can't use await or then. My suggested example is just for non-async flows, something like using forwarding refs!

Async Functions
Forwarding Refs

I have the same problem of the @tomkis.

When call setTimeout(form.reset) the global form state is cleared, but the fields states stay with touched and others properties preserved.

A very ugly solution is to call form.resetFieldState for all fields.

    const onSubmit = useCallback(async (values, form) => {
        try {
            await axios.post(...);

            setTimeout(() => {
                Object.keys(values).forEach(key => {
                    form.resetFieldState(key);
                });
                form.reset();
            });
        } catch (err) {
            ...
        }
    }, []);

    return (
        <Form
            onSubmit={onSubmit}
            render={...}
        />
    );

I do it this way:

const [formKey, setFormKey] = useState(Date.now());
...
<Form
  key={formKey}
  ...
  onSubmit={() => {
    // save to server
   setFormKey(Date.now());
  }}
/>

I wanted my form to reset fields after form is successfully submitted. This is the solution working in my case:

<Form onSubmit={this.onSubmit}>
  {({handleSubmit, form, submitSucceeded, values}) => {
    if (submitSucceeded) {
      form.reset();
      Object.keys(values).forEach(field => form.resetFieldState(field));
    }
    return (
    < form onSubmit = {handleSubmit} >
      ...
    </ form>
    )
  }}
</Form>

It's a pitty form.reset() does not automatically turn fields touched property to false, which for my form results showing validation errors (fields are required).

Final-form introduced new type in 4.20 version
https://final-form.org/docs/final-form/types/FormApi

Quote from docs:
"restart
() => void
Resets all form and field state. Same as calling reset() on the form and resetFieldState() for each field. Form should be just as it was when it was first created."

Example code:
const onSumbit = form => setTimeout(() => form.restart())
or with destructuring
const onSumbit = { restart } => setTimeout(() => restart())

Remember that your onSubmit function not only receives values, but also the form object:

function MyComponent() {
  const mySubmit = useCallback(
    (values, form) => {
      // Do what you want with your values...

      setTimeout(form.restart); // or setTimeout(form.reset())
    },
    [/* ... */]
  );

  return (
    <Form
      onSubmit={mySubmit}
      component={MyForm}
    />
  );
}

cf. https://final-form.org/docs/react-final-form/types/FormProps#onsubmit

Here's a working sandbox with my suggestion from above.

Edit 馃弫 React Final Form - Reset After Submit

Doesn't seem to work with final-form 4.20.1 and react-final-form 6.5.1. @erikras
Solved it using the second argument of onSubmit function which is the form.

const onSubmit = async (values, form) => {
    handleCalculate();
    setTimeout(() => form.initialize({}), 10);
    form.resetFieldState("amount_known");
    form.resetFieldState("used_electricity_amount");
  }

<Form
      onSubmit={onSubmit}

or form.restart() seems to reset all the fields too.

Another way that works is using form.reset() in the render prop of Form

    <Form
      onSubmit={onSubmit}
      render={({ handleSubmit, values, form }) => (
        < form
          className='form'
          id='real-estate-form-2'
          onSubmit={event => handleSubmit(event).then(() => {
            form.reset();
          })}>

I think that this is the best practice.

final-form handleSubmit method has 3 parameters, values, form and callback.
You need to point which method will be called on callback,, and you can add form.reset for that.
In this sample, I'm collecting only edited values, and sending them to my handleFormSubmit async method.

```
validate={validateFields}
onSubmit={(values, form, callback) => {
// eslint-disable-next-line no-param-reassign
callback = form.reset;
const changedValues = {};
Object.keys(form.getState().dirtyFields).forEach(propName => {
changedValues[propName] = values[propName]
});
return handleFormSubmit(changedValues);
}}

There is no more need to do this kind of workaround, I made a PR that allows the reset during the submission: https://github.com/final-form/final-form/pull/363/files#diff-582693474d2a90e13161b9d64da11dd53a1e4a811df74d614d87463fffa1c8d7

You can now simply do:

const onSubmit = (values, form) => {
  // what do you want
  form.reset();
}

Wouldn't it make more sense to use form.initialize so you keep the submitSuccess state? You can pass an empty object or whatever you previous used for initial values. I think this also sets the form state of pristine to true again too so you can use that state with submitSuccess to determine the first submission vs second etc.

  const onSubmit = useCallback(
    async function onChangePasswordFormSubmit(values, form) {
      try {
        await updatePassword(values)
        // clears focus if submit via enter press
        document.activeElement.blur()
        // re-initialize form AFTER submitSuccess to prevent re-focus on first field
        setTimeout(() => form.initialize({}), 20) // resets form without losing submitSucceeded state
        return undefined // triggers submitSuccess state
      } catch (error) {
        console.error(error)
        return mapErrors(error)
      }
    },
    [ updatePassword]
  )

It depends on the need, everyone can do an initialize, a reset or a restart.

I don't really see the interest of the setTimeout and the return here.

Was this page helpful?
0 / 5 - 0 ratings