Repository: erikras/redux-form
Pull request #589 (2016-01-26 04:08:26)

Disallow async validation when the form is pristine
This fix addresses an issue where forms with `asyncValidate` and `asyncBlurFields` specified call `asyncValidate` on every blur, even when the form has not been changed from its initial values.

@erikras please let me know if you have any other ideas on how to handle this one.


Patch:
diff --git a/src/createHigherOrderComponent.js b/src/createHigherOrderComponent.js
index 02eee2c1..9d141fa0 100644
--- a/src/createHigherOrderComponent.js
+++ b/src/createHigherOrderComponent.js
@@ -66,9 +66,10 @@ const createHigherOrderComponent = (config,
             values[name] = value;
           }
           const syncErrors = validate(values, this.props);
+          const { allPristine } = this.fields._meta;
 
-          // if blur validating, only run async validate if sync validation passes
-          if (!name || isValid(syncErrors[name])) {
+          // if blur validating, only run async validate if the form is dirty and sync validation passes
+          if (!allPristine && (!name || isValid(syncErrors[name]))) {
             return asyncValidation(() =>
               asyncValidate(values, dispatch, this.props), startAsyncValidation, stopAsyncValidation, name);
           }


Test patch:
diff --git a/src/__tests__/createReduxForm.spec.js b/src/__tests__/createReduxForm.spec.js
index 70f0fd02..a4fae301 100644
--- a/src/__tests__/createReduxForm.spec.js
+++ b/src/__tests__/createReduxForm.spec.js
@@ -1589,6 +1589,40 @@ describe('createReduxForm', () => {
     });
   });
 
+  it('should disallow async validation when form is pristine', () => {
+    const store = makeStore();
+    const form = 'testForm';
+    const asyncValidate = createSpy(() => Promise.resolve());
+    const Decorated = reduxForm({
+      form,
+      fields: ['name'],
+      initialValues: {name: 'Tom'},
+      asyncValidate
+    })(Form);
+    const dom = TestUtils.renderIntoDocument(
+      <Provider store={store}>
+        <Decorated/>
+      </Provider>
+    );
+    const stub = TestUtils.findRenderedComponentWithType(dom, Form);
+
+    // check field before validation
+    expectField({
+      field: stub.props.fields.name,
+      name: 'name',
+      value: 'Tom',
+      initial: 'Tom',
+      valid: true,
+      dirty: false,
+      error: undefined,
+      touched: false,
+      visited: false
+    });
+
+    stub.props.asyncValidate();
+    expect(asyncValidate).toNotHaveBeenCalled();
+  });
+
   it('should allow deep async validation error values', () => {
     const store = makeStore();
     const form = 'testForm';
@@ -1621,6 +1655,10 @@ describe('createReduxForm', () => {
       touched: false,
       visited: false
     });
+
+    // form must be dirty for asyncValidate()
+    stub.props.fields.name.onChange('Moe');
+
     return stub.props.asyncValidate()
       .then(() => {
         expect(true).toBe(false); // should not be in success block
@@ -1629,17 +1667,17 @@ describe('createReduxForm', () => {
         expect(store.getState().form.testForm.name)
           .toEqual({
             initial: 'Tom',
-            value: 'Tom',
+            value: 'Moe',
             asyncError: deepError
           });
         // check field
         expectField({
           field: stub.props.fields.name,
           name: 'name',
-          value: 'Tom',
+          value: 'Moe',
           initial: 'Tom',
           valid: false,
-          dirty: false,
+          dirty: true,
           error: deepError,
           touched: false,
           visited: false