deferredComputed.spec.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { computed, deferredComputed, effect, ref } from '../src'
  2. describe('deferred computed', () => {
  3. const tick = Promise.resolve()
  4. test('should only trigger once on multiple mutations', async () => {
  5. const src = ref(0)
  6. const c = deferredComputed(() => src.value)
  7. const spy = jest.fn()
  8. effect(() => {
  9. spy(c.value)
  10. })
  11. expect(spy).toHaveBeenCalledTimes(1)
  12. src.value = 1
  13. src.value = 2
  14. src.value = 3
  15. // not called yet
  16. expect(spy).toHaveBeenCalledTimes(1)
  17. await tick
  18. // should only trigger once
  19. expect(spy).toHaveBeenCalledTimes(2)
  20. expect(spy).toHaveBeenCalledWith(c.value)
  21. })
  22. test('should not trigger if value did not change', async () => {
  23. const src = ref(0)
  24. const c = deferredComputed(() => src.value % 2)
  25. const spy = jest.fn()
  26. effect(() => {
  27. spy(c.value)
  28. })
  29. expect(spy).toHaveBeenCalledTimes(1)
  30. src.value = 1
  31. src.value = 2
  32. await tick
  33. // should not trigger
  34. expect(spy).toHaveBeenCalledTimes(1)
  35. src.value = 3
  36. src.value = 4
  37. src.value = 5
  38. await tick
  39. // should trigger because latest value changes
  40. expect(spy).toHaveBeenCalledTimes(2)
  41. })
  42. test('chained computed trigger', async () => {
  43. const effectSpy = jest.fn()
  44. const c1Spy = jest.fn()
  45. const c2Spy = jest.fn()
  46. const src = ref(0)
  47. const c1 = deferredComputed(() => {
  48. c1Spy()
  49. return src.value % 2
  50. })
  51. const c2 = computed(() => {
  52. c2Spy()
  53. return c1.value + 1
  54. })
  55. effect(() => {
  56. effectSpy(c2.value)
  57. })
  58. expect(c1Spy).toHaveBeenCalledTimes(1)
  59. expect(c2Spy).toHaveBeenCalledTimes(1)
  60. expect(effectSpy).toHaveBeenCalledTimes(1)
  61. src.value = 1
  62. await tick
  63. expect(c1Spy).toHaveBeenCalledTimes(2)
  64. expect(c2Spy).toHaveBeenCalledTimes(2)
  65. expect(effectSpy).toHaveBeenCalledTimes(2)
  66. })
  67. test('chained computed avoid re-compute', async () => {
  68. const effectSpy = jest.fn()
  69. const c1Spy = jest.fn()
  70. const c2Spy = jest.fn()
  71. const src = ref(0)
  72. const c1 = deferredComputed(() => {
  73. c1Spy()
  74. return src.value % 2
  75. })
  76. const c2 = computed(() => {
  77. c2Spy()
  78. return c1.value + 1
  79. })
  80. effect(() => {
  81. effectSpy(c2.value)
  82. })
  83. expect(effectSpy).toHaveBeenCalledTimes(1)
  84. src.value = 2
  85. src.value = 4
  86. src.value = 6
  87. await tick
  88. // c1 should re-compute once.
  89. expect(c1Spy).toHaveBeenCalledTimes(2)
  90. // c2 should not have to re-compute because c1 did not change.
  91. expect(c2Spy).toHaveBeenCalledTimes(1)
  92. // effect should not trigger because c2 did not change.
  93. expect(effectSpy).toHaveBeenCalledTimes(1)
  94. })
  95. test('chained computed value invalidation', async () => {
  96. const effectSpy = jest.fn()
  97. const c1Spy = jest.fn()
  98. const c2Spy = jest.fn()
  99. const src = ref(0)
  100. const c1 = deferredComputed(() => {
  101. c1Spy()
  102. return src.value % 2
  103. })
  104. const c2 = deferredComputed(() => {
  105. c2Spy()
  106. return c1.value + 1
  107. })
  108. effect(() => {
  109. effectSpy(c2.value)
  110. })
  111. expect(effectSpy).toHaveBeenCalledTimes(1)
  112. expect(effectSpy).toHaveBeenCalledWith(1)
  113. expect(c2.value).toBe(1)
  114. expect(c1Spy).toHaveBeenCalledTimes(1)
  115. expect(c2Spy).toHaveBeenCalledTimes(1)
  116. src.value = 1
  117. // value should be available sync
  118. expect(c2.value).toBe(2)
  119. expect(c2Spy).toHaveBeenCalledTimes(2)
  120. })
  121. test('sync access of invalidated chained computed should not prevent final effect from running', async () => {
  122. const effectSpy = jest.fn()
  123. const c1Spy = jest.fn()
  124. const c2Spy = jest.fn()
  125. const src = ref(0)
  126. const c1 = deferredComputed(() => {
  127. c1Spy()
  128. return src.value % 2
  129. })
  130. const c2 = deferredComputed(() => {
  131. c2Spy()
  132. return c1.value + 1
  133. })
  134. effect(() => {
  135. effectSpy(c2.value)
  136. })
  137. expect(effectSpy).toHaveBeenCalledTimes(1)
  138. src.value = 1
  139. // sync access c2
  140. c2.value
  141. await tick
  142. expect(effectSpy).toHaveBeenCalledTimes(2)
  143. })
  144. test('should not compute if deactivated before scheduler is called', async () => {
  145. const c1Spy = jest.fn()
  146. const src = ref(0)
  147. const c1 = deferredComputed(() => {
  148. c1Spy()
  149. return src.value % 2
  150. })
  151. effect(() => c1.value)
  152. expect(c1Spy).toHaveBeenCalledTimes(1)
  153. c1.effect.stop()
  154. // trigger
  155. src.value++
  156. await tick
  157. expect(c1Spy).toHaveBeenCalledTimes(1)
  158. })
  159. })