ref.spec.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import {
  2. ref,
  3. effect,
  4. reactive,
  5. isRef,
  6. toRefs,
  7. Ref,
  8. isReactive
  9. } from '../src/index'
  10. import { computed } from '@vue/runtime-dom'
  11. import { shallowRef, unref } from '../src/ref'
  12. describe('reactivity/ref', () => {
  13. it('should hold a value', () => {
  14. const a = ref(1)
  15. expect(a.value).toBe(1)
  16. a.value = 2
  17. expect(a.value).toBe(2)
  18. })
  19. it('should be reactive', () => {
  20. const a = ref(1)
  21. let dummy
  22. effect(() => {
  23. dummy = a.value
  24. })
  25. expect(dummy).toBe(1)
  26. a.value = 2
  27. expect(dummy).toBe(2)
  28. })
  29. it('should make nested properties reactive', () => {
  30. const a = ref({
  31. count: 1
  32. })
  33. let dummy
  34. effect(() => {
  35. dummy = a.value.count
  36. })
  37. expect(dummy).toBe(1)
  38. a.value.count = 2
  39. expect(dummy).toBe(2)
  40. })
  41. it('should work without initial value', () => {
  42. const a = ref()
  43. let dummy
  44. effect(() => {
  45. dummy = a.value
  46. })
  47. expect(dummy).toBe(undefined)
  48. a.value = 2
  49. expect(dummy).toBe(2)
  50. })
  51. it('should work like a normal property when nested in a reactive object', () => {
  52. const a = ref(1)
  53. const obj = reactive({
  54. a,
  55. b: {
  56. c: a
  57. }
  58. })
  59. let dummy1: number
  60. let dummy2: number
  61. effect(() => {
  62. dummy1 = obj.a
  63. dummy2 = obj.b.c
  64. })
  65. const assertDummiesEqualTo = (val: number) =>
  66. [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
  67. assertDummiesEqualTo(1)
  68. a.value++
  69. assertDummiesEqualTo(2)
  70. obj.a++
  71. assertDummiesEqualTo(3)
  72. obj.b.c++
  73. assertDummiesEqualTo(4)
  74. })
  75. it('should unwrap nested ref in types', () => {
  76. const a = ref(0)
  77. const b = ref(a)
  78. expect(typeof (b.value + 1)).toBe('number')
  79. })
  80. it('should unwrap nested values in types', () => {
  81. const a = {
  82. b: ref(0)
  83. }
  84. const c = ref(a)
  85. expect(typeof (c.value.b + 1)).toBe('number')
  86. })
  87. it('should NOT unwrap ref types nested inside arrays', () => {
  88. const arr = ref([1, ref(1)]).value
  89. ;(arr[0] as number)++
  90. ;(arr[1] as Ref<number>).value++
  91. const arr2 = ref([1, new Map<string, any>(), ref('1')]).value
  92. const value = arr2[0]
  93. if (isRef(value)) {
  94. value + 'foo'
  95. } else if (typeof value === 'number') {
  96. value + 1
  97. } else {
  98. // should narrow down to Map type
  99. // and not contain any Ref type
  100. value.has('foo')
  101. }
  102. })
  103. it('should keep tuple types', () => {
  104. const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
  105. 0,
  106. '1',
  107. { a: 1 },
  108. () => 0,
  109. ref(0)
  110. ]
  111. const tupleRef = ref(tuple)
  112. tupleRef.value[0]++
  113. expect(tupleRef.value[0]).toBe(1)
  114. tupleRef.value[1] += '1'
  115. expect(tupleRef.value[1]).toBe('11')
  116. tupleRef.value[2].a++
  117. expect(tupleRef.value[2].a).toBe(2)
  118. expect(tupleRef.value[3]()).toBe(0)
  119. tupleRef.value[4].value++
  120. expect(tupleRef.value[4].value).toBe(1)
  121. })
  122. test('unref', () => {
  123. expect(unref(1)).toBe(1)
  124. expect(unref(ref(1))).toBe(1)
  125. })
  126. test('shallowRef', () => {
  127. const sref = shallowRef({ a: 1 })
  128. expect(isReactive(sref.value)).toBe(false)
  129. let dummy
  130. effect(() => {
  131. dummy = sref.value.a
  132. })
  133. expect(dummy).toBe(1)
  134. sref.value = { a: 2 }
  135. expect(isReactive(sref.value)).toBe(false)
  136. expect(dummy).toBe(2)
  137. })
  138. test('isRef', () => {
  139. expect(isRef(ref(1))).toBe(true)
  140. expect(isRef(computed(() => 1))).toBe(true)
  141. expect(isRef(0)).toBe(false)
  142. expect(isRef(1)).toBe(false)
  143. // an object that looks like a ref isn't necessarily a ref
  144. expect(isRef({ value: 0 })).toBe(false)
  145. })
  146. test('toRefs', () => {
  147. const a = reactive({
  148. x: 1,
  149. y: 2
  150. })
  151. const { x, y } = toRefs(a)
  152. expect(isRef(x)).toBe(true)
  153. expect(isRef(y)).toBe(true)
  154. expect(x.value).toBe(1)
  155. expect(y.value).toBe(2)
  156. // source -> proxy
  157. a.x = 2
  158. a.y = 3
  159. expect(x.value).toBe(2)
  160. expect(y.value).toBe(3)
  161. // proxy -> source
  162. x.value = 3
  163. y.value = 4
  164. expect(a.x).toBe(3)
  165. expect(a.y).toBe(4)
  166. // reactivity
  167. let dummyX, dummyY
  168. effect(() => {
  169. dummyX = x.value
  170. dummyY = y.value
  171. })
  172. expect(dummyX).toBe(x.value)
  173. expect(dummyY).toBe(y.value)
  174. // mutating source should trigger effect using the proxy refs
  175. a.x = 4
  176. a.y = 5
  177. expect(dummyX).toBe(4)
  178. expect(dummyY).toBe(5)
  179. })
  180. })