Repository: final-form/react-final-form
Pull request #520 (2019-06-11 07:59:13)

Allowed swappable final-form APIs
[Suggested in a tweet](https://twitter.com/Syynth/status/1138204720974241792)

> @erikras do you have any interest in allowing the React bindings for @finalformjs to receive the FormApi instance as a prop? I'm interested in it for testing purposes but I could imagine it also having value if someone wanted to pass a custom FF compatible API impl.

It took me a minute to understand what was being asked, but when I did, it seemed like a pretty cool idea. Just as Final Form doesn't mind what view framework consumes it, React Final Form shouldn't mind what underlying engine that comports to the Final Form API is being used.

codecov[bot]: # [Codecov](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=h1) Report
> Merging [#520](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=desc) into [master](https://codecov.io/gh/final-form/react-final-form/commit/9fe62c56b0eebeb1a5e61fa67b03d2ca4037cd2c?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/final-form/react-final-form/pull/520/graphs/tree.svg?width=650&token=s8VUe6IxMZ&height=150&src=pr)](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #520   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          16     16           
  Lines         232    232           
  Branches       56     57    +1     
=====================================
  Hits          232    232
```


| [Impacted Files](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/ReactFinalForm.js](https://codecov.io/gh/final-form/react-final-form/pull/520/diff?src=pr&el=tree#diff-c3JjL1JlYWN0RmluYWxGb3JtLmpz) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=footer). Last update [9fe62c5...dc235ec](https://codecov.io/gh/final-form/react-final-form/pull/520?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/ReactFinalForm.js b/src/ReactFinalForm.js
index a586568..e68fb4e 100644
--- a/src/ReactFinalForm.js
+++ b/src/ReactFinalForm.js
@@ -42,6 +42,7 @@ function ReactFinalForm<FormValues: FormValuesShape>({
   debug,
   decorators,
   destroyOnUnregister,
+  form: alternateFormApi,
   initialValues,
   initialValuesEqual,
   keepDirtyOnReinitialize,
@@ -65,7 +66,7 @@ function ReactFinalForm<FormValues: FormValuesShape>({
   }
 
   const form: FormApi<FormValues> = useConstant(() => {
-    const f = createForm<FormValues>(config)
+    const f = alternateFormApi || createForm<FormValues>(config)
     f.pauseValidation()
     return f
   })
diff --git a/src/types.js.flow b/src/types.js.flow
index b974c76..85574a1 100644
--- a/src/types.js.flow
+++ b/src/types.js.flow
@@ -68,6 +68,7 @@ export type RenderableProps<T> = {
 export type FormProps<FormValues: FormValuesShape> = {
   subscription?: FormSubscription,
   decorators?: Decorator<FormValues>[],
+  form?: FormApi<FormValues>,
   initialValuesEqual?: (?Object, ?Object) => boolean
 } & Config<FormValues> &
   RenderableProps<FormRenderProps<FormValues>>
diff --git a/typescript/index.d.ts b/typescript/index.d.ts
index 3194b40..b0cce9e 100644
--- a/typescript/index.d.ts
+++ b/typescript/index.d.ts
@@ -60,6 +60,7 @@ export interface FormProps<FormValues = object>
     RenderableProps<FormRenderProps<FormValues>> {
   subscription?: FormSubscription;
   decorators?: Decorator[];
+  form?: FormApi<FormValues>;
   initialValuesEqual?: (a?: object, b?: object) => boolean;
 }
 


Test patch:
diff --git a/src/ReactFinalForm.test.js b/src/ReactFinalForm.test.js
index 94aa74f..e80ac3d 100644
--- a/src/ReactFinalForm.test.js
+++ b/src/ReactFinalForm.test.js
@@ -3,6 +3,7 @@ import { render, fireEvent, cleanup } from 'react-testing-library'
 import 'jest-dom/extend-expect'
 import deepEqual from 'fast-deep-equal'
 import { ErrorBoundary, Toggle, wrapWith } from './testUtils'
+import { createForm } from 'final-form'
 import Form from './ReactFinalForm'
 import Field from './Field'
 
@@ -907,4 +908,27 @@ describe('ReactFinalForm', () => {
     expect(recordSubmitting).toHaveBeenCalledTimes(3)
     expect(recordSubmitting.mock.calls[2][0]).toBe(false)
   })
+
+  it('should allow an alternative form api to be passed in', () => {
+    const onSubmit = jest.fn()
+    const form = createForm({ onSubmit: onSubmit })
+    const formMock = jest.spyOn(form, 'registerField')
+    render(
+      <Form form={form}>
+        {({ handleSubmit }) => (
+          <form onSubmit={handleSubmit}>
+            <Field name="name" component="input" />
+          </form>
+        )}
+      </Form>
+    )
+    expect(formMock).toHaveBeenCalled()
+
+    // called once on first render to get initial state, and then again to subscribe
+    expect(formMock).toHaveBeenCalledTimes(2)
+    expect(formMock.mock.calls[0][0]).toBe('name')
+    expect(formMock.mock.calls[0][2].active).toBe(true) // default subscription
+    expect(formMock.mock.calls[1][0]).toBe('name')
+    expect(formMock.mock.calls[1][2].active).toBe(true) // default subscription
+  })
 })