Repository: final-form/react-final-form
Pull request #503 (2019-05-28 16:10:03)

Fix Flow typings for UseFieldConfig and FieldProps
There was a mismatch between the source code and Flow typings which led to the following error:
```js
<Field
  name={name}
  component={SomeCustomInputComponent}
  /* ^^^ Flow error here:
  Cannot create Field element because in property component:
   • Either function [1] is incompatible with string literal
     input [2].
   • Or function [1] is incompatible with string literal
     select [3].
   • Or function [1] is incompatible with string literal
     textarea [4].
  */
/>
```

<!--

👋 Hey, thanks for your interest in contributing to 🏁 React Final Form!

**Please ask first before starting work on any significant new features.**

It's never a fun experience to have your pull request declined after investing a
lot of time and effort into a new feature. To avoid this from happening, we
request that contributors create an issue to first discuss any significant new
features.

Please try to keep your pull request focused in scope and avoid including
unrelated commits.

After you have submitted your pull request, we’ll try to get back to you as soon
as possible. We may suggest some changes or improvements.

Please format the code before submitting your pull request by running:

```
npm run precommit
```

https://github.com/final-form/react-final-form/blob/master/.github/CONTRIBUTING.md

-->


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

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

```diff
@@          Coverage Diff          @@
##           master   #503   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          14     14           
  Lines         231    231           
  Branches       57     57           
=====================================
  Hits          231    231
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/final-form/react-final-form/pull/503?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/503?src=pr&el=footer). Last update [13008e3...4629353](https://codecov.io/gh/final-form/react-final-form/pull/503?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

erikras: I feel like ignoring `SupportedInputs` is a mistake. Seems like an important shortlist of valid strings.

Patch:
diff --git a/README.md b/README.md
index 3c1c320..bde5116 100644
--- a/README.md
+++ b/README.md
@@ -585,6 +585,8 @@ returns an error if the value is invalid, or `undefined` if the value is valid.
 
 [See the 🏁 Final Form docs on `validateFields`](https://github.com/final-form/final-form#validatefields-string).
 
+⚠️ IMPORTANT ⚠️ – By default, in order to allow inline `[]` syntax, the field will not rerender if you change your `validateFields` prop changes. If you need your field to rerender with a new `validateFields` setting, you will need to update another prop on the `Field`, such as `key`.
+
 #### `value?: any`
 
 **This is only used for checkboxes and radio buttons!**
diff --git a/src/index.js.flow b/src/index.js.flow
index 4ba2e2f..b40ad1b 100644
--- a/src/index.js.flow
+++ b/src/index.js.flow
@@ -10,6 +10,15 @@ import type {
   UseFormStateParams
 } from './types'
 
+export type {
+  FieldProps,
+  FieldRenderProps,
+  FormProps,
+  FormRenderProps,
+  FormSpyProps,
+  FormSpyRenderProps
+} from './types'
+
 declare export var Field: React.ComponentType<FieldProps>
 declare export var Form: React.ComponentType<FormProps>
 declare export var FormSpy: React.ComponentType<FormSpyProps>
diff --git a/src/types.js.flow b/src/types.js.flow
index 759437c..238d97a 100644
--- a/src/types.js.flow
+++ b/src/types.js.flow
@@ -75,7 +75,8 @@ export type UseFieldConfig = {
   afterSubmit?: () => void,
   allowNull?: boolean,
   beforeSubmit?: () => void | false,
-  component?: SupportedInputs,
+  children?: $PropertyType<RenderableProps<*>, 'children'>,
+  component?: $PropertyType<RenderableProps<*>, 'component'>,
   defaultValue?: any,
   format?: (value: any, name: string) => any,
   formatOnBlur?: boolean,
@@ -92,9 +93,7 @@ export type UseFieldConfig = {
 
 export type FieldProps = UseFieldConfig & {
   name: string
-} & RenderableProps<FieldRenderProps> & {
-    component?: React.ComponentType<*> | SupportedInputs
-  }
+} & RenderableProps<FieldRenderProps>
 
 export type UseFormStateParams = {
   onChange?: (formState: FormState) => void,
diff --git a/src/useField.js b/src/useField.js
index 790d7f1..e6ac6c9 100644
--- a/src/useField.js
+++ b/src/useField.js
@@ -98,8 +98,11 @@ const useField = (
       // cannot reregister field every time validate function !==.
       // validate,
       initialValue,
-      isEqual,
-      validateFields
+      isEqual
+      // The validateFields array is often passed as validateFields={[]}, creating
+      // a !== new array every time. If it needs to be changed, a rerender/reregister
+      // can be forced by changing the key prop
+      // validateFields
     ]
   )
 
diff --git a/typescript/index.d.ts b/typescript/index.d.ts
index ff553b1..d2dc7f9 100644
--- a/typescript/index.d.ts
+++ b/typescript/index.d.ts
@@ -102,7 +102,7 @@ export const Form: React.FC<FormProps<object>>;
 export const FormSpy: React.FC<FormSpyProps>;
 export function useField<T extends HTMLElement>(
   name: string,
-  config: UseFieldConfig
+  config?: UseFieldConfig
 ): FieldRenderProps<T>;
 export function useForm(componentName?: string): FormApi;
 export function useFormState(params?: UseFormStateParams): FormState;


Test patch:
diff --git a/src/Field.test.js b/src/Field.test.js
index cf8cd48..544c108 100644
--- a/src/Field.test.js
+++ b/src/Field.test.js
@@ -501,6 +501,31 @@ describe('Field', () => {
     expect(getByTestId('error')).toHaveTextContent('')
   })
 
+  it('should not rerender if validateFields is !== every time', () => {
+    // https://github.com/final-form/react-final-form/issues/502
+    const required = value => (value ? undefined : 'Required')
+    const spy = jest.fn()
+    const { getByTestId } = render(
+      <Form onSubmit={onSubmitMock}>
+        {({ handleSubmit }) => (
+          <form onSubmit={handleSubmit}>
+            <Field name="name" validate={required} validateFields={[]}>
+              {wrapWith(spy, ({ input, meta }) => (
+                <div>
+                  <input {...input} data-testid="name" />
+                  <div data-testid="error">{meta.error}</div>
+                </div>
+              ))}
+            </Field>
+          </form>
+        )}
+      </Form>
+    )
+    // first render registered validation, second contains error
+    expect(spy).toHaveBeenCalledTimes(2)
+    expect(getByTestId('error')).toHaveTextContent('Required')
+  })
+
   it('should pass along type prop', () => {
     const { getByTestId } = render(
       <Form onSubmit={onSubmitMock}>
diff --git a/src/renderComponent.test.js b/src/renderComponent.test.js
index 0149744..877b57c 100644
--- a/src/renderComponent.test.js
+++ b/src/renderComponent.test.js
@@ -13,4 +13,29 @@ describe('renderComponent', () => {
     const result = renderComponent(props, name)
     expect(result.props).toEqual({ children, render })
   })
+
+  it('should include children when rendering with render', () => {
+    const children = 'some children'
+    const render = jest.fn()
+    const props = {
+      children,
+      render
+    }
+    const name = 'TestComponent'
+    renderComponent(props, name)
+    expect(render).toHaveBeenCalled()
+    expect(render).toHaveBeenCalledTimes(1)
+    expect(render.mock.calls[0][0].children).toBe(children)
+  })
+
+  it('should throw error if no render strategy is provided', () => {
+    const children = 'some children'
+    const props = {
+      children
+    }
+    const name = 'TestComponent'
+    expect(() => renderComponent(props, name)).toThrow(
+      `Must specify either a render prop, a render function as children, or a component prop to ${name}`
+    )
+  })
 })