ref.spec.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. import {
  2. ref,
  3. effect,
  4. reactive,
  5. isRef,
  6. toRef,
  7. toRefs,
  8. Ref,
  9. isReactive
  10. } from '../src/index'
  11. import { computed } from '@vue/runtime-dom'
  12. import { shallowRef, unref, customRef, triggerRef } from '../src/ref'
  13. describe('reactivity/ref', () => {
  14. it('should hold a value', () => {
  15. const a = ref(1)
  16. expect(a.value).toBe(1)
  17. a.value = 2
  18. expect(a.value).toBe(2)
  19. })
  20. it('should be reactive', () => {
  21. const a = ref(1)
  22. let dummy
  23. let calls = 0
  24. effect(() => {
  25. calls++
  26. dummy = a.value
  27. })
  28. expect(calls).toBe(1)
  29. expect(dummy).toBe(1)
  30. a.value = 2
  31. expect(calls).toBe(2)
  32. expect(dummy).toBe(2)
  33. // same value should not trigger
  34. a.value = 2
  35. expect(calls).toBe(2)
  36. expect(dummy).toBe(2)
  37. })
  38. it('should make nested properties reactive', () => {
  39. const a = ref({
  40. count: 1
  41. })
  42. let dummy
  43. effect(() => {
  44. dummy = a.value.count
  45. })
  46. expect(dummy).toBe(1)
  47. a.value.count = 2
  48. expect(dummy).toBe(2)
  49. })
  50. it('should work without initial value', () => {
  51. const a = ref()
  52. let dummy
  53. effect(() => {
  54. dummy = a.value
  55. })
  56. expect(dummy).toBe(undefined)
  57. a.value = 2
  58. expect(dummy).toBe(2)
  59. })
  60. it('should work like a normal property when nested in a reactive object', () => {
  61. const a = ref(1)
  62. const obj = reactive({
  63. a,
  64. b: {
  65. c: a
  66. }
  67. })
  68. let dummy1: number
  69. let dummy2: number
  70. effect(() => {
  71. dummy1 = obj.a
  72. dummy2 = obj.b.c
  73. })
  74. const assertDummiesEqualTo = (val: number) =>
  75. [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
  76. assertDummiesEqualTo(1)
  77. a.value++
  78. assertDummiesEqualTo(2)
  79. obj.a++
  80. assertDummiesEqualTo(3)
  81. obj.b.c++
  82. assertDummiesEqualTo(4)
  83. })
  84. it('should unwrap nested ref in types', () => {
  85. const a = ref(0)
  86. const b = ref(a)
  87. expect(typeof (b.value + 1)).toBe('number')
  88. })
  89. it('should unwrap nested values in types', () => {
  90. const a = {
  91. b: ref(0)
  92. }
  93. const c = ref(a)
  94. expect(typeof (c.value.b + 1)).toBe('number')
  95. })
  96. it('should NOT unwrap ref types nested inside arrays', () => {
  97. const arr = ref([1, ref(1)]).value
  98. ;(arr[0] as number)++
  99. ;(arr[1] as Ref<number>).value++
  100. const arr2 = ref([1, new Map<string, any>(), ref('1')]).value
  101. const value = arr2[0]
  102. if (isRef(value)) {
  103. value + 'foo'
  104. } else if (typeof value === 'number') {
  105. value + 1
  106. } else {
  107. // should narrow down to Map type
  108. // and not contain any Ref type
  109. value.has('foo')
  110. }
  111. })
  112. it('should keep tuple types', () => {
  113. const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
  114. 0,
  115. '1',
  116. { a: 1 },
  117. () => 0,
  118. ref(0)
  119. ]
  120. const tupleRef = ref(tuple)
  121. tupleRef.value[0]++
  122. expect(tupleRef.value[0]).toBe(1)
  123. tupleRef.value[1] += '1'
  124. expect(tupleRef.value[1]).toBe('11')
  125. tupleRef.value[2].a++
  126. expect(tupleRef.value[2].a).toBe(2)
  127. expect(tupleRef.value[3]()).toBe(0)
  128. tupleRef.value[4].value++
  129. expect(tupleRef.value[4].value).toBe(1)
  130. })
  131. it('should keep symbols', () => {
  132. const customSymbol = Symbol()
  133. const obj = {
  134. [Symbol.asyncIterator]: { a: 1 },
  135. [Symbol.unscopables]: { b: '1' },
  136. [customSymbol]: { c: [1, 2, 3] }
  137. }
  138. const objRef = ref(obj)
  139. expect(objRef.value[Symbol.asyncIterator]).toBe(obj[Symbol.asyncIterator])
  140. expect(objRef.value[Symbol.unscopables]).toBe(obj[Symbol.unscopables])
  141. expect(objRef.value[customSymbol]).toStrictEqual(obj[customSymbol])
  142. })
  143. test('unref', () => {
  144. expect(unref(1)).toBe(1)
  145. expect(unref(ref(1))).toBe(1)
  146. })
  147. test('shallowRef', () => {
  148. const sref = shallowRef({ a: 1 })
  149. expect(isReactive(sref.value)).toBe(false)
  150. let dummy
  151. effect(() => {
  152. dummy = sref.value.a
  153. })
  154. expect(dummy).toBe(1)
  155. sref.value = { a: 2 }
  156. expect(isReactive(sref.value)).toBe(false)
  157. expect(dummy).toBe(2)
  158. })
  159. test('shallowRef force trigger', () => {
  160. const sref = shallowRef({ a: 1 })
  161. let dummy
  162. effect(() => {
  163. dummy = sref.value.a
  164. })
  165. expect(dummy).toBe(1)
  166. sref.value.a = 2
  167. expect(dummy).toBe(1) // should not trigger yet
  168. // force trigger
  169. triggerRef(sref)
  170. expect(dummy).toBe(2)
  171. })
  172. test('isRef', () => {
  173. expect(isRef(ref(1))).toBe(true)
  174. expect(isRef(computed(() => 1))).toBe(true)
  175. expect(isRef(0)).toBe(false)
  176. expect(isRef(1)).toBe(false)
  177. // an object that looks like a ref isn't necessarily a ref
  178. expect(isRef({ value: 0 })).toBe(false)
  179. })
  180. test('toRef', () => {
  181. const a = reactive({
  182. x: 1
  183. })
  184. const x = toRef(a, 'x')
  185. expect(isRef(x)).toBe(true)
  186. expect(x.value).toBe(1)
  187. // source -> proxy
  188. a.x = 2
  189. expect(x.value).toBe(2)
  190. // proxy -> source
  191. x.value = 3
  192. expect(a.x).toBe(3)
  193. // reactivity
  194. let dummyX
  195. effect(() => {
  196. dummyX = x.value
  197. })
  198. expect(dummyX).toBe(x.value)
  199. // mutating source should trigger effect using the proxy refs
  200. a.x = 4
  201. expect(dummyX).toBe(4)
  202. })
  203. test('toRefs', () => {
  204. const a = reactive({
  205. x: 1,
  206. y: 2
  207. })
  208. const { x, y } = toRefs(a)
  209. expect(isRef(x)).toBe(true)
  210. expect(isRef(y)).toBe(true)
  211. expect(x.value).toBe(1)
  212. expect(y.value).toBe(2)
  213. // source -> proxy
  214. a.x = 2
  215. a.y = 3
  216. expect(x.value).toBe(2)
  217. expect(y.value).toBe(3)
  218. // proxy -> source
  219. x.value = 3
  220. y.value = 4
  221. expect(a.x).toBe(3)
  222. expect(a.y).toBe(4)
  223. // reactivity
  224. let dummyX, dummyY
  225. effect(() => {
  226. dummyX = x.value
  227. dummyY = y.value
  228. })
  229. expect(dummyX).toBe(x.value)
  230. expect(dummyY).toBe(y.value)
  231. // mutating source should trigger effect using the proxy refs
  232. a.x = 4
  233. a.y = 5
  234. expect(dummyX).toBe(4)
  235. expect(dummyY).toBe(5)
  236. })
  237. test('customRef', () => {
  238. let value = 1
  239. let _trigger: () => void
  240. const custom = customRef((track, trigger) => ({
  241. get() {
  242. track()
  243. return value
  244. },
  245. set(newValue: number) {
  246. value = newValue
  247. _trigger = trigger
  248. }
  249. }))
  250. expect(isRef(custom)).toBe(true)
  251. let dummy
  252. effect(() => {
  253. dummy = custom.value
  254. })
  255. expect(dummy).toBe(1)
  256. custom.value = 2
  257. // should not trigger yet
  258. expect(dummy).toBe(1)
  259. _trigger!()
  260. expect(dummy).toBe(2)
  261. })
  262. })