Repository: erikras/redux-form
Pull request #323 (2015-11-22 23:02:36)

Normalization of keyed forms.
Reducer's getValues now returns values. I assume this was a bug.

Normalization can now occur on formKeyed forms, if the action.key is defined.

Also, every version of eslint I tried gave me an error for redeclaration in on master.

I didn't add any tests for this, so please let me know if you want some, assuming that is was the right approach to take.


Patch:
diff --git a/.eslintrc b/.eslintrc
index 05439245..6971b571 100755
--- a/.eslintrc
+++ b/.eslintrc
@@ -12,7 +12,7 @@
     "comma-dangle": 0,  // not sure why airbnb turned this on. gross!
     "indent": [2, 2, {"SwitchCase": 1}],
     "spaced-comment": 0,
-
+    "no-redeclare": 0,
     //Temporarirly disabled due to a possible bug in babel-eslint (todomvc example)
     "block-scoped-var": 0,
     // Temporarily disabled for test/* until babel/babel-eslint#33 is resolved
diff --git a/src/reducer.js b/src/reducer.js
index 65c90fee..e42d4166 100644
--- a/src/reducer.js
+++ b/src/reducer.js
@@ -227,22 +227,31 @@ function decorate(target) {
       return {
         ...result,
         ...mapValues(normalizers, (formNormalizers, form) => {
-          const previousValues = getValuesFromState({...initialState, ...state[form]});
-          const formResult = {
-            ...initialState,
-            ...result[form]
-          };
-          return {
-            ...formResult,
-            ...mapValues(formNormalizers, (fieldNormalizer, field) => ({
-              ...formResult[field],
-              value: fieldNormalizer(
-                formResult[field] ? formResult[field].value : undefined,                  // value
-                state[form] && state[form][field] ? state[form][field].value : undefined, // previous value
-                getValuesFromState(formResult),                                           // all field values
-                previousValues)                                                           // all previous field values
-            }))
+          const runNormalize = (previous, currentResult) => {
+            const previousValues = getValuesFromState({...initialState, ...previous
+            });
+            const formResult = {
+              ...initialState,
+              ...currentResult
+            };
+            return {
+              ...formResult,
+              ...mapValues(formNormalizers, (fieldNormalizer, field) => ({
+                ...formResult[field],
+                value: fieldNormalizer(
+                    formResult[field] ? formResult[field].value : undefined,         // value
+                    previous && previous[field] ? previous[field].value : undefined, // previous value
+                    getValuesFromState(formResult),                                           // all field values
+                    previousValues)                                                  // all previous field values
+              }))
+            };
           };
+          if (action.key) {
+            return {
+              ...result[form], [action.key]: runNormalize(state[form][action.key], result[form][action.key])
+            };
+          }
+          return runNormalize(state[form], result[form]);
         })
       };
     });


Test patch:
diff --git a/src/__tests__/isPristine.spec.js b/src/__tests__/isPristine.spec.js
index 73f4094a..a8fbd135 100644
--- a/src/__tests__/isPristine.spec.js
+++ b/src/__tests__/isPristine.spec.js
@@ -41,6 +41,7 @@ describe('isPristine', () => {
     const date1 = new Date();
     const date2 = new Date(date1.getTime() + 1);
     tryBothWays({date: date1}, {date: date2}, false);
+
   });
 
   it('should return false when the number of keys is different', () => {
diff --git a/src/__tests__/reducer.spec.js b/src/__tests__/reducer.spec.js
index 9db7f069..82f20185 100644
--- a/src/__tests__/reducer.spec.js
+++ b/src/__tests__/reducer.spec.js
@@ -1455,4 +1455,61 @@ describe('reducer.normalize', () => {
         }
       });
   });
+  it('should normalize keyed forms depending on action form key', () => {
+    const defaultFields = {
+      _active: undefined,
+      _asyncValidating: false,
+      _error: undefined,
+      _submitting: false,
+      _submitFailed: false,
+    };
+    const normalize = reducer.normalize({
+      foo: {
+        myField: () => 'normalized'
+      }
+    });
+    const state = normalize({
+      foo: {
+        firstSubform: {}
+      }
+    }, {
+      form: 'foo',
+      key: 'firstSubform'
+    });
+    const nextState = normalize(state, {
+      form: 'foo',
+      key: 'secondSubForm'
+    });
+    expect(state)
+      .toExist()
+      .toBeA('object');
+    expect(Object.keys(state).length).toBe(1);
+    expect(state.foo)
+      .toExist()
+      .toBeA('object')
+      .toEqual({
+        firstSubform: {
+          ...defaultFields,
+          myField: {
+            value: 'normalized'
+          }
+        }
+      });
+    expect(nextState.foo)
+      .toEqual({
+        firstSubform: {
+          ...defaultFields,
+          myField: {
+            value: 'normalized'
+          }
+        },
+        secondSubForm: {
+          ...defaultFields,
+          myField: {
+            value: 'normalized'
+          }
+        }
+      });
+
+  });
 });