Repository: jaredpalmer/formik
Pull request #806 (2018-08-03 03:57:00)

Fix FastField for number input with empty value
Issue:
FastField have an issue in handling input with type number or range when the value is empty.
It triggers infinite loop warnings "The specified value "NaN" is not a valid number...".

Here is the demo of issue: [https://codesandbox.io/s/n4rlmq208p](https://codesandbox.io/s/n4rlmq208p)

Solution:
if input value is '' for number or range input, don't parseFloat it. 


Patch:
diff --git a/src/FastField.tsx b/src/FastField.tsx
index 5c2f8ee..d60395c 100644
--- a/src/FastField.tsx
+++ b/src/FastField.tsx
@@ -99,8 +99,9 @@ class FastFieldInner<Props = {}, Values = {}> extends React.Component<
       setFormikState,
     } = this.props.formik;
     const { type, value, checked } = e.target;
+    let parsed;
     const val = /number|range/.test(type)
-      ? parseFloat(value)
+      ? ((parsed = parseFloat(value)), isNaN(parsed) ? '' : parsed)
       : /checkbox/.test(type) ? checked : value;
     if (validateOnChange) {
       // Field-level validation
diff --git a/src/Formik.tsx b/src/Formik.tsx
old mode 100644
new mode 100755
index 9517a1e..fee5d71
--- a/src/Formik.tsx
+++ b/src/Formik.tsx
@@ -493,7 +493,7 @@ export class Formik<ExtraProps = {}, Values = object> extends React.Component<
     );
   };
 
-  setFieldError = (field: string, message: string) => {
+  setFieldError = (field: string, message: string | undefined) => {
     // Set form field by name
     this.setState(prevState => ({
       ...prevState,
diff --git a/src/utils.ts b/src/utils.ts
old mode 100644
new mode 100755
index e25538b..68e20d5
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -43,8 +43,21 @@ export function setIn(obj: any, path: string, value: any): any {
     }
   }
 
-  resVal[pathArray[i]] = value;
-  return { ...obj, ...res };
+  if (value === undefined) {
+    delete resVal[pathArray[i]];
+  } else {
+    resVal[pathArray[i]] = value;
+  }
+
+  const result = { ...obj, ...res };
+
+  // If the path array has a single element, the loop did not run.
+  // Deleting on `resVal` had no effect in this scenario, so we delete on the result instead.
+  if (i === 0 && value === undefined) {
+    delete result[pathArray[i]];
+  }
+
+  return result;
 }
 
 /**


Test patch:
diff --git a/test/utils.test.tsx b/test/utils.test.tsx
old mode 100644
new mode 100755
index de243d7..b16dff1
--- a/test/utils.test.tsx
+++ b/test/utils.test.tsx
@@ -134,6 +134,14 @@ describe('utils', () => {
       expect(newObj).toEqual({ x: 'y', flat: 'value' });
     });
 
+    it('removes flat value', () => {
+      const obj = { x: 'y' };
+      const newObj = setIn(obj, 'x', undefined);
+      expect(obj).toEqual({ x: 'y' });
+      expect(newObj).toEqual({});
+      expect(newObj).not.toHaveProperty('x');
+    });
+
     it('sets nested value', () => {
       const obj = { x: 'y' };
       const newObj = setIn(obj, 'nested.value', 'nested value');
@@ -148,6 +156,14 @@ describe('utils', () => {
       expect(newObj).toEqual({ x: 'y', nested: { value: 'b' } });
     });
 
+    it('removes nested value', () => {
+      const obj = { x: 'y', nested: { value: 'a' } };
+      const newObj = setIn(obj, 'nested.value', undefined);
+      expect(obj).toEqual({ x: 'y', nested: { value: 'a' } });
+      expect(newObj).toEqual({ x: 'y', nested: {} });
+      expect(newObj.nested).not.toHaveProperty('value');
+    });
+
     it('updates deep nested value', () => {
       const obj = { x: 'y', twofoldly: { nested: { value: 'a' } } };
       const newObj = setIn(obj, 'twofoldly.nested.value', 'b');
@@ -156,6 +172,15 @@ describe('utils', () => {
       expect(newObj).toEqual({ x: 'y', twofoldly: { nested: { value: 'b' } } }); // works ofc
     });
 
+    it('removes deep nested value', () => {
+      const obj = { x: 'y', twofoldly: { nested: { value: 'a' } } };
+      const newObj = setIn(obj, 'twofoldly.nested.value', undefined);
+      expect(obj.twofoldly.nested === newObj.twofoldly.nested).toEqual(false);
+      expect(obj).toEqual({ x: 'y', twofoldly: { nested: { value: 'a' } } });
+      expect(newObj).toEqual({ x: 'y', twofoldly: { nested: {} } });
+      expect(newObj.twofoldly.nested).not.toHaveProperty('value');
+    });
+
     it('sets new array', () => {
       const obj = { x: 'y' };
       const newObj = setIn(obj, 'nested.0', 'value');