Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle submission validation errors #5778

Merged
merged 1 commit into from
Jan 20, 2021

Conversation

alanpoulain
Copy link
Contributor

@alanpoulain alanpoulain commented Jan 14, 2021

Fixes #4703.
Fixes #4351.

Returning a promise in the submit function is essential for handling correctly submission errors: https://final-form.org/docs/react-final-form/types/FormProps#3-asynchronous-with-a-promise.

It has been discussed in this issue too: #4351.

useCreateController and useEditController hooks are already returning a promise resolving to undefined for the save function: it will not break anything.

To handle submission errors, the custom save function could be implemented by using the useDataProvider hook, like this: bmihelac@1a9f02b.

In order to be able to use the useMutation hook too, a returnPromise option has been added, like asked in @fzaninotto's comment: #4703 (comment).

I hope it will be OK, it would be really nice to have a way to handle submission errors easily in React-Admin and API Platform Admin!

@alanpoulain alanpoulain changed the base branch from master to next January 14, 2021 22:34
Copy link
Member

@fzaninotto fzaninotto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Can you add some usage documentation in https://marmelab.com/react-admin/CreateEdit.html#validation, and a unit test in useMutation.spec.tsx?

@alanpoulain alanpoulain force-pushed the feat-save-promise branch 2 times, most recently from ef37260 to 4714fd4 Compare January 19, 2021 11:11
@alanpoulain
Copy link
Contributor Author

alanpoulain commented Jan 19, 2021

PR has been updated to add:

  • documentation
  • a unit test for useMutation
  • allowing save to be defined in SimpleForm
  • handle submitError in inputs

I've tested it on a project. With this code:

const Create = (props) => {
  const [mutate] = useMutation()
  const save = useCallback(
    async (values) => {
      try {
        await mutate({
          type: 'create',
          resource: 'origins',
          payload: { data: values },
        }, { returnPromise: true });
      } catch (error) {
        return {
          url: 'a custom server error',
        };
      }
    },
    [mutate],
  )

  return (
    <Wrapper>
      <RACreate undoable={false} {...props}>
        <SimpleForm toolbar={<Toolbar />} save={save} redirect='/origins'>
          <InputGuesser source='name' fullWidth variant='outlined' />
          <InputGuesser source='url' fullWidth variant='outlined' />
          <InputGuesser source='comment' fullWidth variant='outlined' />
        </SimpleForm>
      </RACreate>
    </Wrapper>
  )
}

The result is:

submission_errors.mp4

@alanpoulain alanpoulain changed the title Return the save promise in the submit function Handle submission validation errors Jan 19, 2021
@alanpoulain alanpoulain force-pushed the feat-save-promise branch 3 times, most recently from 959ea9a to 964b7ab Compare January 19, 2021 15:37
@alanpoulain
Copy link
Contributor Author

Rebased against next.
The unit test useQueryWithStore › should refetch the dataProvider on refresh seems to be flaky.
Locally, sometimes it passes, sometimes not:
image
image

form.restart(initialValuesMergedWithRecord);
form.setConfig('keepDirtyOnReinitialize', true);
// Since the submit function returns a promise, use setTimeout to prevent the error "Cannot reset() in onSubmit()" in final-form
setTimeout(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really concerning to me. Any error in the form.restart() won't be caught.

Can you explain why the error occurs, why the setTimeout removes it, and check if there is no other way? And can you add a unit test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the way final-form is telling to do it: https://github.com/final-form/final-form/blob/ee1ef7272882a966644ca1bf0ea6f115a2492251/src/FinalForm.js#L933.
However this constraint seems to have been removed in the next version:
final-form/final-form#363
I don't see how we could add a unit test for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment to explain that the setTimeout should be removed when the next version of final-form will be released.
And actually there is already a test for this:

it('should not display a warning about unsaved changes when an array input has been updated', () => {
ListPagePosts.navigate();
ListPagePosts.nextPage(); // Ensure the record is visible in the table
EditPostPage.navigate();
// Select first notification input checkbox
cy.get(
EditPostPage.elements.input('notifications', 'checkbox-group-input')
)
.eq(0)
.click();
EditPostPage.submit();
// If the update succeeded without display a warning about unsaved changes,
// we should have been redirected to the list
cy.url().then(url => expect(url).to.contain('/#/posts'));
});

(it throws the linked error without the setTimeout)

@djhi
Copy link
Collaborator

djhi commented Jan 20, 2021

The unit test useQueryWithStore › should refetch the dataProvider on refresh seems to be flaky

Indeed. Looking into it

@fzaninotto fzaninotto merged commit 1e29aed into marmelab:next Jan 20, 2021
@fzaninotto
Copy link
Member

Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle Promises from onSave in FormWithRedirect
3 participants