react-final-form icon indicating copy to clipboard operation
react-final-form copied to clipboard

Pass external variable to onSubmit

Open ignatevdev opened this issue 6 years ago • 13 comments

Are you submitting a bug report or a feature request?

Feature request

What is the current behavior?

onSubmit only receives predefined arguments without an option to pass some custom data through handleSubmit() or form.submit()

What is the expected behavior?

It would be very useful if handleSubmit could accept more than one variable, and pass these variables to the onSubmit call.

Use case

Consider having a form with two actions: Save and Send to moderation

In both cases the form should first go through basic validation, then save the data, but if user has chosen to "Send to moderation", we should make an additional validation check and require more fields, and if this validation passes - make one more api call.

It is currently possible to take the values from the form's render props and make a custom callback. However, the only non-hacky way to throw submit errors is to return them from onSubmit. Therefore, unless we are able to pass some custom data, in this case - user's action, we would not be able to achieve the desired behaviour without using some hacks like saving the action via the useRef before calling onSubmit.

ignatevdev avatar Oct 23 '19 11:10 ignatevdev

You can make the Send to moderation button type=button which will not submit the form, but call your custom function which will run submit and call whatever additional logic is needed.

SpadarShut avatar Oct 24 '19 10:10 SpadarShut

You can make the Send to moderation button type=button which will not submit the form, but call your custom function which will run submit and call whatever additional logic is needed.

Can you provide an example? I'm stuck on this also

rocketkittens avatar Jan 28 '20 01:01 rocketkittens

You can make the Send to moderation button type=button which will not submit the form, but call your custom function which will run submit and call whatever additional logic is needed.

Can you provide an example? I'm stuck on this also

I think Something like this should work

const sendToModeration = (values) => {}; 
const submitForm = (values) => {}; 

return <Form onSubmit={(values) => {
 // default submit code
submitForm(values);
}>
  {({ handleSubmit, form: { getState } }) => <form onSubmit={handleSubmit}>
  <input type="submit" />
  <input type="button" onClick={() => sendToModeration(getState().values}} />
</form>}
</Form>

alfondotnet avatar Dec 15 '20 00:12 alfondotnet

Having a similar problem, there is no way to run server side validation and bind back errors. I figured out i can swap onSubmit but issue i'm having with that is that you can't get original onSubmit to restore it. From what i understand you can't do that from mutator either

() => {
  setConfig('onSubmit', async (values) => {
    console.log('submit', values);
  });
  submit().finally(
    () => {
      // setConfig('onSubmit', originalSubmit)
    }
  )
}

shulcsm avatar Jan 19 '21 09:01 shulcsm

I would also love to have this feature to pass a variable to onSubmit() via handleSubmit()

But seems like RFF is a little dead, which is sad because it is really awesome without all the hidden gotchas and slow performance I have seen in other state-form management libraries...

andrwo avatar Apr 13 '21 06:04 andrwo

Are you submitting a bug report or a feature request?

Feature request

What is the current behavior?

onSubmit only receives predefined arguments without an option to pass some custom data through handleSubmit() or form.submit()

What is the expected behavior?

It would be very useful if handleSubmit could accept more than one variable, and pass these variables to the onSubmit call.

Use case

Consider having a form with two actions: Save and Send to moderation

In both cases the form should first go through basic validation, then save the data, but if user has chosen to "Send to moderation", we should make an additional validation check and require more fields, and if this validation passes - make one more api call.

It is currently possible to take the values from the form's render props and make a custom callback. However, the only non-hacky way to throw submit errors is to return them from onSubmit. Therefore, unless we are able to pass some custom data, in this case - user's action, we would not be able to achieve the desired behaviour without using some hacks like saving the action via the useRef before calling onSubmit.

@ignatevdev This would be useful. I ran into the exact same challenge! I would like to pass an extra argument to onSubmit/handleSubmit then use it inside the submit handler to control what to do with the server response. How did you solve this with useRef?

chaiwa-berian avatar May 11 '21 08:05 chaiwa-berian

Another approach is to use some field as container for your custom option:

const handleSubmit = async ({ action, ...values }, form) => {
  if (action === "delete") {
    ...
  } else {
    ...
  }
}
...
<Form onSubmit={handleSubmit}>
  {({
    form: {
      change,
    },
    submitting,
  }) => (
    <DeleteButton
      type="submit"
      onClick={() => change("action", "delete")}
      disabled={submitting}
    >
      Delete
    </DeleteButton>
    <Button
      type="submit"
      onClick={() => change("action", "save")}
      disabled={submitting}
      isLoading={submitting}
      label="Save"
    />
  )}
</Form>

Yep, its not about refs, but you can pass some flag and make condition inside submit based on its value.

BATCOH avatar May 11 '21 09:05 BATCOH

Another approach is to use some field as container for your custom option:

const handleSubmit = async ({ action, ...values }, form) => {
  if (action === "delete") {
    ...
  } else {
    ...
  }
}
...
<Form onSubmit={handleSubmit}>
  {({
    form: {
      change,
    },
    submitting,
  }) => (
    <DeleteButton
      type="submit"
      onClick={() => change("action", "delete")}
      disabled={submitting}
    >
      Delete
    </DeleteButton>
    <Button
      type="submit"
      onClick={() => change("action", "save")}
      disabled={submitting}
      isLoading={submitting}
      label="Save"
    />
  )}
</Form>

Yep, its not about refs, but you can pass some flag and make condition inside submit based on its value.

@batcoh, this looks good but what if I do not want to touch form state? Like a common no-op action but should take place after submit succeeds?

chaiwa-berian avatar May 11 '21 09:05 chaiwa-berian

Is that solution even race-safe? Would change block and ensure state before submitting?

shulcsm avatar May 11 '21 10:05 shulcsm

@shulcsm I think it safe enough, I use it in production. onSubmit fires only after onClick handler is done and only if click event is not prevented by event.preventDefault. FormAPI.change is synchronous. However, there is no guarantee, AFAIK events order is not described in the standards and depends on browser implementation. But it works well in my tests and with my use cases. I made a minimal example in Codesandbox, you can try it on your own.

BATCOH avatar May 11 '21 11:05 BATCOH

@chaiwa-berian I personally use final-form-submit-listener to make something in separate handler after submit succeeded. And I still can check for "action" (or any other field) value in this handler via FormAPI.

BATCOH avatar May 11 '21 12:05 BATCOH

Not sure if it would be recommended, but the onSubmit and other parts of the formApi can be configured dynamically using setConfig. setConfig doesn't seem to be listed in the documentation, but it's there in the code. I found this to work to implement different submission strategies:

const onSave = () => { ... }
const onSend = () => { ... }

const { setConfig, submit } = useForm();

const onClickSave = () => {
    setConfig('onSubmit', onSave);
    submit();
}

const onClickSend = () => {
    setConfig('onSubmit', onSend);
    submit();
}

return (
    <>
        <Button onClick={onClickSave}>Save</Button>
        <Button onClick={onClickSend}>Send</Button>
    </>
);

References:

  • https://github.com/final-form/final-form/blob/3c3029f1d5b75a939bd4d6edaf560aac0dadc724/src/types.js.flow#L232
  • https://github.com/final-form/react-final-form/blob/147bd62fc86b9cf7e0e7300605a5ff0be2f90e2d/src/ReactFinalForm.js#L156

metchel avatar Jun 01 '22 02:06 metchel