Repository: final-form/react-final-form
Pull request #497 (2019-05-27 09:46:45)

Outlaw subscription changes
**BREAKING CHANGE**

We used to support resubscribing when the `subscription` prop changed. That was needlessly complicated. If you need to change the subscription, you should force a reregister by also changing the `key` prop.


Patch:
diff --git a/src/ReactFinalForm.js b/src/ReactFinalForm.js
index f3096cf..f74d5c7 100644
--- a/src/ReactFinalForm.js
+++ b/src/ReactFinalForm.js
@@ -19,7 +19,6 @@ import useConstant from './useConstant'
 import shallowEqual from './shallowEqual'
 import isSyntheticEvent from './isSyntheticEvent'
 import type { FormRenderProps } from './types.js.flow'
-import flattenSubscription from './flattenSubscription'
 import ReactFinalFormContext from './context'
 
 export const version = '5.1.0'
@@ -79,8 +78,6 @@ const ReactFinalForm = ({
     }
   )
 
-  // In the future, changing subscriptions on the fly should be banned. ⚠️
-  const flattenedSubscription = flattenSubscription(subscription)
   React.useEffect(() => {
     // We have rendered, so all fields are no registered, so we can unpause validation
     form.isValidationPaused() && form.resumeValidation()
@@ -103,7 +100,7 @@ const ReactFinalForm = ({
       unsubscriptions.forEach(unsubscribe => unsubscribe())
     }
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [decorators, flattenedSubscription, state])
+  }, [decorators])
 
   // warn about decorator changes
   // istanbul ignore next
diff --git a/src/flattenSubscription.js b/src/flattenSubscription.js
deleted file mode 100644
index 87b9408..0000000
--- a/src/flattenSubscription.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// @flow
-type Subscription = { [string]: boolean }
-export default function flattenSubscription(
-  subscription: Subscription = {}
-): string {
-  return Object.keys(subscription)
-    .filter(key => subscription[key] === true)
-    .map(key => key)
-    .sort()
-    .join(',')
-}
diff --git a/src/useField.js b/src/useField.js
index 3d5dc6a..790d7f1 100644
--- a/src/useField.js
+++ b/src/useField.js
@@ -1,7 +1,6 @@
 // @flow
 import * as React from 'react'
 import { fieldSubscriptionItems } from 'final-form'
-import flattenSubscription from './flattenSubscription'
 import type { FieldSubscription, FieldState, FormApi } from 'final-form'
 import type { UseFieldConfig, FieldInputProps, FieldRenderProps } from './types'
 import isReactNative from './isReactNative'
@@ -82,8 +81,6 @@ const useField = (
     return beforeSubmit && beforeSubmit()
   }
 
-  // In the future, changing subscriptions on the fly should be banned. ⚠️
-  const flattenedSubscription = flattenSubscription(subscription)
   React.useEffect(
     () =>
       register(state => {
@@ -102,9 +99,7 @@ const useField = (
       // validate,
       initialValue,
       isEqual,
-      validateFields,
-      // eslint-disable-next-line react-hooks/exhaustive-deps
-      flattenedSubscription
+      validateFields
     ]
   )
 
diff --git a/src/useFormState.js b/src/useFormState.js
index a0af358..69293aa 100644
--- a/src/useFormState.js
+++ b/src/useFormState.js
@@ -1,6 +1,5 @@
 // @flow
 import * as React from 'react'
-import flattenSubscription from './flattenSubscription'
 import type { UseFormStateParams } from './types'
 import type { FormState, FormApi } from 'final-form'
 import { all } from './ReactFinalForm'
@@ -27,8 +26,6 @@ const useFormState = ({
     }
   )
 
-  // In the future, changing subscriptions on the fly should be banned. ⚠️
-  const flattenedSubscription = flattenSubscription(subscription)
   React.useEffect(
     () =>
       form.subscribe(newState => {
@@ -42,7 +39,7 @@ const useFormState = ({
         }
       }, subscription),
     // eslint-disable-next-line react-hooks/exhaustive-deps
-    [flattenedSubscription]
+    []
   )
   return state
 }


Test patch:
diff --git a/src/FormSpy.test.js b/src/FormSpy.test.js
index 77f5160..aa3ed3d 100644
--- a/src/FormSpy.test.js
+++ b/src/FormSpy.test.js
@@ -97,7 +97,7 @@ describe('FormSpy', () => {
     expect(spy.mock.calls[2][0].values).toEqual({ name: 'erikras' })
   })
 
-  it('should resubscribe if subscription changes', () => {
+  it('should NOT resubscribe if subscription changes', () => {
     const firstSubscription = { values: true, pristine: true }
     const secondSubscription = { dirty: true, submitting: true }
     const spy = jest.fn()
@@ -165,19 +165,8 @@ describe('FormSpy', () => {
 
     fireEvent.click(getByText('Toggle'))
 
-    // one for new prop, and again after reregistering
-    expect(spy).toHaveBeenCalledTimes(4)
-    hasFormApi(spy.mock.calls[3][0])
-    expect(spy.mock.calls[3][0].dirty).toBe(false)
-    expect(spy.mock.calls[3][0].errors).toBeUndefined()
-    expect(spy.mock.calls[3][0].invalid).toBeUndefined()
-    expect(spy.mock.calls[3][0].pristine).toBeUndefined()
-    expect(spy.mock.calls[3][0].submitFailed).toBeUndefined()
-    expect(spy.mock.calls[3][0].submitSucceeded).toBeUndefined()
-    expect(spy.mock.calls[3][0].submitting).toBe(false)
-    expect(spy.mock.calls[3][0].valid).toBeUndefined()
-    expect(spy.mock.calls[3][0].validating).toBeUndefined()
-    expect(spy.mock.calls[3][0].values).toBeUndefined()
+    // one for new prop, and NOT again because no reregistering since v6
+    expect(spy).toHaveBeenCalledTimes(3)
   })
 
   it('should hear changes', () => {
diff --git a/src/ReactFinalForm.test.js b/src/ReactFinalForm.test.js
index f90163a..c725707 100644
--- a/src/ReactFinalForm.test.js
+++ b/src/ReactFinalForm.test.js
@@ -1,5 +1,5 @@
 import React from 'react'
-import { render, fireEvent, cleanup } from 'react-testing-library'
+import { render, fireEvent, cleanup, act } from 'react-testing-library'
 import 'jest-dom/extend-expect'
 import deepEqual from 'fast-deep-equal'
 import { ErrorBoundary, Toggle, wrapWith } from './testUtils'
diff --git a/src/flattenSubscription.test.js b/src/flattenSubscription.test.js
deleted file mode 100644
index 7242f5e..0000000
--- a/src/flattenSubscription.test.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import flattenSubscription from './flattenSubscription'
-
-describe('flattenSubscription', () => {
-  it('should return empty array when subscription is empty', () => {
-    expect(flattenSubscription(undefined)).toEqual('')
-    expect(flattenSubscription({})).toEqual('')
-  })
-
-  it('should return only keys that are true in subscription', () => {
-    expect(flattenSubscription({ foo: true, bar: false })).toEqual('foo')
-    expect(flattenSubscription({ foo: true, bar: true, baz: false })).toEqual(
-      'bar,foo'
-    )
-  })
-})