]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): account for NaN in value change checks (#361)
authorMayness <1095346833@qq.com>
Wed, 23 Oct 2019 15:53:43 +0000 (10:53 -0500)
committerEvan You <yyx990803@gmail.com>
Wed, 23 Oct 2019 15:53:43 +0000 (11:53 -0400)
packages/reactivity/__tests__/collections/Map.spec.ts
packages/reactivity/__tests__/collections/WeakMap.spec.ts
packages/reactivity/__tests__/effect.spec.ts
packages/reactivity/src/baseHandlers.ts
packages/reactivity/src/collectionHandlers.ts
packages/runtime-core/src/apiWatch.ts
packages/shared/src/index.ts

index 0ae0965bb1e704187dad6ff7e229f481f7f40125..b190da32fce513e5b709f95669b2a16e711768ce 100644 (file)
@@ -311,5 +311,13 @@ describe('reactivity/collections', () => {
       map.get(key)!.foo++
       expect(dummy).toBe(2)
     })
+
+    it('should not be trigger when the value and the old value both are NaN', () => {
+      const map = reactive(new Map([['foo', NaN]]))
+      const mapSpy = jest.fn(() => map.get('foo'))
+      effect(mapSpy)
+      map.set('foo', NaN)
+      expect(mapSpy).toHaveBeenCalledTimes(1)
+    })
   })
 })
index 01e729569bdaead75f4a4edb13ed98654aa17e73..6c82b82dd867d9837d70250d14fbe08437ed99e8 100644 (file)
@@ -107,5 +107,15 @@ describe('reactivity/collections', () => {
       observed.get(key).a = 2
       expect(dummy).toBe(2)
     })
+
+    it('should not be trigger when the value and the old value both are NaN', () => {
+      const map = new WeakMap()
+      const key = {}
+      map.set(key, NaN)
+      const mapSpy = jest.fn(() => map.get(key))
+      effect(mapSpy)
+      map.set(key, NaN)
+      expect(mapSpy).toHaveBeenCalledTimes(1)
+    })
   })
 })
index 32a7fbdfea29f94a59b2c69f6e528f34166405c6..9ddd8e059f720a6bfb5f71b2880d4d4b496fbd52 100644 (file)
@@ -691,4 +691,14 @@ describe('reactivity/effect', () => {
     obj.foo = { prop: 1 }
     expect(dummy).toBe(1)
   })
+
+  it('should not be trigger when the value and the old value both are NaN', () => {
+    const obj = reactive({
+      foo: NaN
+    })
+    const fnSpy = jest.fn(() => obj.foo)
+    effect(fnSpy)
+    obj.foo = NaN
+    expect(fnSpy).toHaveBeenCalledTimes(1)
+  })
 })
index f75c5b74b44475438deb8a904c5ceed51b8fd87b..4f48644513ea8fb3028420e0ffef3d70196eb203 100644 (file)
@@ -2,7 +2,7 @@ import { reactive, readonly, toRaw } from './reactive'
 import { OperationTypes } from './operations'
 import { track, trigger } from './effect'
 import { LOCKED } from './lock'
-import { isObject, hasOwn, isSymbol } from '@vue/shared'
+import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared'
 import { isRef } from './ref'
 
 const builtInSymbols = new Set(
@@ -52,13 +52,13 @@ function set(
       const extraInfo = { oldValue, newValue: value }
       if (!hadKey) {
         trigger(target, OperationTypes.ADD, key, extraInfo)
-      } else if (value !== oldValue) {
+      } else if (hasChanged(value, oldValue)) {
         trigger(target, OperationTypes.SET, key, extraInfo)
       }
     } else {
       if (!hadKey) {
         trigger(target, OperationTypes.ADD, key)
-      } else if (value !== oldValue) {
+      } else if (hasChanged(value, oldValue)) {
         trigger(target, OperationTypes.SET, key)
       }
     }
index 7f208583ef80bebac7336514713de266c9f368fa..9e315261e64851df6750afa49bcf73a0d802092e 100644 (file)
@@ -2,7 +2,7 @@ import { toRaw, reactive, readonly } from './reactive'
 import { track, trigger } from './effect'
 import { OperationTypes } from './operations'
 import { LOCKED } from './lock'
-import { isObject, capitalize, hasOwn } from '@vue/shared'
+import { isObject, capitalize, hasOwn, hasChanged } from '@vue/shared'
 
 export type CollectionTypes = IterableCollections | WeakCollections
 
@@ -73,13 +73,13 @@ function set(this: MapTypes, key: unknown, value: unknown) {
     const extraInfo = { oldValue, newValue: value }
     if (!hadKey) {
       trigger(target, OperationTypes.ADD, key, extraInfo)
-    } else if (value !== oldValue) {
+    } else if (hasChanged(value, oldValue)) {
       trigger(target, OperationTypes.SET, key, extraInfo)
     }
   } else {
     if (!hadKey) {
       trigger(target, OperationTypes.ADD, key)
-    } else if (value !== oldValue) {
+    } else if (hasChanged(value, oldValue)) {
       trigger(target, OperationTypes.SET, key)
     }
   }
index f374940e955037581d7aedc09b471ef20fd4b1cc..4a8248aa0a365ba677345cae9c944d0783c01dfb 100644 (file)
@@ -7,7 +7,14 @@ import {
   ReactiveEffectOptions
 } from '@vue/reactivity'
 import { queueJob } from './scheduler'
-import { EMPTY_OBJ, isObject, isArray, isFunction, isString } from '@vue/shared'
+import {
+  EMPTY_OBJ,
+  isObject,
+  isArray,
+  isFunction,
+  isString,
+  hasChanged
+} from '@vue/shared'
 import { recordEffect } from './apiReactivity'
 import {
   currentInstance,
@@ -144,7 +151,7 @@ function doWatch(
           return
         }
         const newValue = runner()
-        if (deep || newValue !== oldValue) {
+        if (deep || hasChanged(newValue, oldValue)) {
           // cleanup before running cb again
           if (cleanup) {
             cleanup()
index b83e0884926434393ead79d2a01131672fbcf8c1..32836f53aee8360f0bf60c0071b15463c78d0a5f 100644 (file)
@@ -67,3 +67,7 @@ export const hyphenate = (str: string): string => {
 export const capitalize = (str: string): string => {
   return str.charAt(0).toUpperCase() + str.slice(1)
 }
+
+// compare whether a value has changed, accounting for NaN.
+export const hasChanged = (value: any, oldValue: any): boolean =>
+  value !== oldValue && (value === value || oldValue === oldValue)