@tanstack/react-query 5.0.0-alpha.9 → 5.0.0-alpha.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. package/build/codemods/remove-overloads/remove-overloads.js +59 -0
  2. package/build/codemods/remove-overloads/transformers/filter-aware-usage-transformer.js +153 -0
  3. package/build/codemods/remove-overloads/transformers/query-fn-aware-usage-transformer.js +188 -0
  4. package/build/codemods/remove-overloads/utils/index.js +124 -0
  5. package/build/codemods/remove-overloads/utils/unknown-usage-error.js +27 -0
  6. package/build/codemods/rename-hydrate/rename-hydrate.js +55 -0
  7. package/build/codemods/rename-properties/rename-properties.js +41 -0
  8. package/build/codemods/src/utils/index.js +205 -0
  9. package/build/codemods/src/v4/key-transformation.js +138 -0
  10. package/build/codemods/src/v4/replace-import-specifier.js +25 -0
  11. package/build/codemods/transformers/query-cache-transformer.js +116 -0
  12. package/build/codemods/transformers/query-client-transformer.js +48 -0
  13. package/build/codemods/transformers/use-query-like-transformer.js +32 -0
  14. package/build/codemods/utils/replacers/key-replacer.js +164 -0
  15. package/build/legacy/HydrationBoundary.cjs +60 -0
  16. package/build/legacy/HydrationBoundary.cjs.map +1 -0
  17. package/build/legacy/HydrationBoundary.d.cts +12 -0
  18. package/build/legacy/HydrationBoundary.d.ts +12 -0
  19. package/build/legacy/HydrationBoundary.js +26 -0
  20. package/build/legacy/HydrationBoundary.js.map +1 -0
  21. package/build/legacy/QueryClientProvider.cjs +71 -0
  22. package/build/legacy/QueryClientProvider.cjs.map +1 -0
  23. package/build/legacy/QueryClientProvider.d.cts +12 -0
  24. package/build/legacy/QueryClientProvider.d.ts +12 -0
  25. package/build/legacy/QueryClientProvider.js +35 -0
  26. package/build/legacy/QueryClientProvider.js.map +1 -0
  27. package/build/legacy/QueryErrorResetBoundary.cjs +66 -0
  28. package/build/legacy/QueryErrorResetBoundary.cjs.map +1 -0
  29. package/build/legacy/QueryErrorResetBoundary.d.cts +14 -0
  30. package/build/legacy/QueryErrorResetBoundary.d.ts +14 -0
  31. package/build/legacy/QueryErrorResetBoundary.js +31 -0
  32. package/build/legacy/QueryErrorResetBoundary.js.map +1 -0
  33. package/build/legacy/errorBoundaryUtils.cjs +67 -0
  34. package/build/legacy/errorBoundaryUtils.cjs.map +1 -0
  35. package/build/legacy/errorBoundaryUtils.d.cts +14 -0
  36. package/build/legacy/errorBoundaryUtils.d.ts +14 -0
  37. package/build/legacy/errorBoundaryUtils.js +31 -0
  38. package/build/legacy/errorBoundaryUtils.js.map +1 -0
  39. package/build/legacy/index.cjs +85 -0
  40. package/build/legacy/index.cjs.map +1 -0
  41. package/build/legacy/index.d.cts +17 -0
  42. package/build/legacy/index.d.ts +17 -0
  43. package/build/legacy/index.js +46 -0
  44. package/build/legacy/index.js.map +1 -0
  45. package/build/legacy/infiniteQueryOptions.cjs +33 -0
  46. package/build/legacy/infiniteQueryOptions.cjs.map +1 -0
  47. package/build/legacy/infiniteQueryOptions.d.cts +13 -0
  48. package/build/legacy/infiniteQueryOptions.d.ts +13 -0
  49. package/build/legacy/infiniteQueryOptions.js +8 -0
  50. package/build/legacy/infiniteQueryOptions.js.map +1 -0
  51. package/build/legacy/isRestoring.cjs +47 -0
  52. package/build/legacy/isRestoring.cjs.map +1 -0
  53. package/build/legacy/isRestoring.d.cts +6 -0
  54. package/build/legacy/isRestoring.d.ts +6 -0
  55. package/build/legacy/isRestoring.js +12 -0
  56. package/build/legacy/isRestoring.js.map +1 -0
  57. package/build/legacy/queryOptions.cjs +33 -0
  58. package/build/legacy/queryOptions.cjs.map +1 -0
  59. package/build/legacy/queryOptions.d.cts +13 -0
  60. package/build/legacy/queryOptions.d.ts +13 -0
  61. package/build/legacy/queryOptions.js +8 -0
  62. package/build/legacy/queryOptions.js.map +1 -0
  63. package/build/legacy/suspense.cjs +48 -0
  64. package/build/legacy/suspense.cjs.map +1 -0
  65. package/build/legacy/suspense.d.cts +10 -0
  66. package/build/legacy/suspense.d.ts +10 -0
  67. package/build/legacy/suspense.js +20 -0
  68. package/build/legacy/suspense.js.map +1 -0
  69. package/build/legacy/types.cjs +19 -0
  70. package/build/legacy/types.cjs.map +1 -0
  71. package/build/legacy/types.d.cts +34 -0
  72. package/build/legacy/types.d.ts +34 -0
  73. package/build/legacy/types.js +1 -0
  74. package/build/legacy/types.js.map +1 -0
  75. package/build/legacy/useBaseQuery.cjs +92 -0
  76. package/build/legacy/useBaseQuery.cjs.map +1 -0
  77. package/build/legacy/useBaseQuery.d.cts +92 -0
  78. package/build/legacy/useBaseQuery.d.ts +92 -0
  79. package/build/legacy/useBaseQuery.js +62 -0
  80. package/build/legacy/useBaseQuery.js.map +1 -0
  81. package/build/legacy/useInfiniteQuery.cjs +41 -0
  82. package/build/legacy/useInfiniteQuery.cjs.map +1 -0
  83. package/build/legacy/useInfiniteQuery.d.cts +8 -0
  84. package/build/legacy/useInfiniteQuery.d.ts +8 -0
  85. package/build/legacy/useInfiniteQuery.js +17 -0
  86. package/build/legacy/useInfiniteQuery.js.map +1 -0
  87. package/build/legacy/useIsFetching.cjs +56 -0
  88. package/build/legacy/useIsFetching.cjs.map +1 -0
  89. package/build/legacy/useIsFetching.d.cts +5 -0
  90. package/build/legacy/useIsFetching.d.ts +5 -0
  91. package/build/legacy/useIsFetching.js +22 -0
  92. package/build/legacy/useIsFetching.js.map +1 -0
  93. package/build/legacy/useMutation.cjs +77 -0
  94. package/build/legacy/useMutation.cjs.map +1 -0
  95. package/build/legacy/useMutation.d.cts +6 -0
  96. package/build/legacy/useMutation.d.ts +6 -0
  97. package/build/legacy/useMutation.js +43 -0
  98. package/build/legacy/useMutation.js.map +1 -0
  99. package/build/legacy/useMutationState.cjs +88 -0
  100. package/build/legacy/useMutationState.cjs.map +1 -0
  101. package/build/legacy/useMutationState.d.cts +10 -0
  102. package/build/legacy/useMutationState.d.ts +10 -0
  103. package/build/legacy/useMutationState.js +53 -0
  104. package/build/legacy/useMutationState.js.map +1 -0
  105. package/build/legacy/useQueries.cjs +128 -0
  106. package/build/legacy/useQueries.cjs.map +1 -0
  107. package/build/legacy/useQueries.d.cts +53 -0
  108. package/build/{lib → legacy}/useQueries.d.ts +14 -11
  109. package/build/legacy/useQueries.js +103 -0
  110. package/build/legacy/useQueries.js.map +1 -0
  111. package/build/legacy/useQuery.cjs +36 -0
  112. package/build/legacy/useQuery.cjs.map +1 -0
  113. package/build/legacy/useQuery.d.cts +8 -0
  114. package/build/legacy/useQuery.d.ts +8 -0
  115. package/build/legacy/useQuery.js +12 -0
  116. package/build/legacy/useQuery.js.map +1 -0
  117. package/build/legacy/useSuspenseInfiniteQuery.cjs +46 -0
  118. package/build/legacy/useSuspenseInfiniteQuery.cjs.map +1 -0
  119. package/build/legacy/useSuspenseInfiniteQuery.d.cts +6 -0
  120. package/build/legacy/useSuspenseInfiniteQuery.d.ts +6 -0
  121. package/build/legacy/useSuspenseInfiniteQuery.js +22 -0
  122. package/build/legacy/useSuspenseInfiniteQuery.js.map +1 -0
  123. package/build/legacy/useSuspenseQuery.cjs +45 -0
  124. package/build/legacy/useSuspenseQuery.cjs.map +1 -0
  125. package/build/legacy/useSuspenseQuery.d.cts +6 -0
  126. package/build/legacy/useSuspenseQuery.d.ts +6 -0
  127. package/build/legacy/useSuspenseQuery.js +21 -0
  128. package/build/legacy/useSuspenseQuery.js.map +1 -0
  129. package/build/legacy/utils.cjs +36 -0
  130. package/build/legacy/utils.cjs.map +1 -0
  131. package/build/legacy/utils.d.cts +3 -0
  132. package/build/legacy/utils.d.ts +3 -0
  133. package/build/legacy/utils.js +11 -0
  134. package/build/legacy/utils.js.map +1 -0
  135. package/build/modern/HydrationBoundary.cjs +60 -0
  136. package/build/modern/HydrationBoundary.cjs.map +1 -0
  137. package/build/modern/HydrationBoundary.d.cts +12 -0
  138. package/build/modern/HydrationBoundary.d.ts +12 -0
  139. package/build/modern/HydrationBoundary.js +26 -0
  140. package/build/modern/HydrationBoundary.js.map +1 -0
  141. package/build/modern/QueryClientProvider.cjs +71 -0
  142. package/build/modern/QueryClientProvider.cjs.map +1 -0
  143. package/build/modern/QueryClientProvider.d.cts +12 -0
  144. package/build/modern/QueryClientProvider.d.ts +12 -0
  145. package/build/modern/QueryClientProvider.js +35 -0
  146. package/build/modern/QueryClientProvider.js.map +1 -0
  147. package/build/modern/QueryErrorResetBoundary.cjs +66 -0
  148. package/build/modern/QueryErrorResetBoundary.cjs.map +1 -0
  149. package/build/modern/QueryErrorResetBoundary.d.cts +14 -0
  150. package/build/modern/QueryErrorResetBoundary.d.ts +14 -0
  151. package/build/modern/QueryErrorResetBoundary.js +31 -0
  152. package/build/modern/QueryErrorResetBoundary.js.map +1 -0
  153. package/build/modern/errorBoundaryUtils.cjs +67 -0
  154. package/build/modern/errorBoundaryUtils.cjs.map +1 -0
  155. package/build/modern/errorBoundaryUtils.d.cts +14 -0
  156. package/build/modern/errorBoundaryUtils.d.ts +14 -0
  157. package/build/modern/errorBoundaryUtils.js +31 -0
  158. package/build/modern/errorBoundaryUtils.js.map +1 -0
  159. package/build/modern/index.cjs +85 -0
  160. package/build/modern/index.cjs.map +1 -0
  161. package/build/modern/index.d.cts +17 -0
  162. package/build/modern/index.d.ts +17 -0
  163. package/build/modern/index.js +46 -0
  164. package/build/modern/index.js.map +1 -0
  165. package/build/modern/infiniteQueryOptions.cjs +33 -0
  166. package/build/modern/infiniteQueryOptions.cjs.map +1 -0
  167. package/build/modern/infiniteQueryOptions.d.cts +13 -0
  168. package/build/modern/infiniteQueryOptions.d.ts +13 -0
  169. package/build/modern/infiniteQueryOptions.js +8 -0
  170. package/build/modern/infiniteQueryOptions.js.map +1 -0
  171. package/build/modern/isRestoring.cjs +47 -0
  172. package/build/modern/isRestoring.cjs.map +1 -0
  173. package/build/modern/isRestoring.d.cts +6 -0
  174. package/build/modern/isRestoring.d.ts +6 -0
  175. package/build/modern/isRestoring.js +12 -0
  176. package/build/modern/isRestoring.js.map +1 -0
  177. package/build/modern/queryOptions.cjs +33 -0
  178. package/build/modern/queryOptions.cjs.map +1 -0
  179. package/build/modern/queryOptions.d.cts +13 -0
  180. package/build/modern/queryOptions.d.ts +13 -0
  181. package/build/modern/queryOptions.js +8 -0
  182. package/build/modern/queryOptions.js.map +1 -0
  183. package/build/modern/suspense.cjs +48 -0
  184. package/build/modern/suspense.cjs.map +1 -0
  185. package/build/modern/suspense.d.cts +10 -0
  186. package/build/modern/suspense.d.ts +10 -0
  187. package/build/modern/suspense.js +20 -0
  188. package/build/modern/suspense.js.map +1 -0
  189. package/build/modern/types.cjs +19 -0
  190. package/build/modern/types.cjs.map +1 -0
  191. package/build/modern/types.d.cts +34 -0
  192. package/build/modern/types.d.ts +34 -0
  193. package/build/modern/types.js +1 -0
  194. package/build/modern/types.js.map +1 -0
  195. package/build/modern/useBaseQuery.cjs +92 -0
  196. package/build/modern/useBaseQuery.cjs.map +1 -0
  197. package/build/modern/useBaseQuery.d.cts +92 -0
  198. package/build/modern/useBaseQuery.d.ts +92 -0
  199. package/build/modern/useBaseQuery.js +62 -0
  200. package/build/modern/useBaseQuery.js.map +1 -0
  201. package/build/modern/useInfiniteQuery.cjs +41 -0
  202. package/build/modern/useInfiniteQuery.cjs.map +1 -0
  203. package/build/modern/useInfiniteQuery.d.cts +8 -0
  204. package/build/modern/useInfiniteQuery.d.ts +8 -0
  205. package/build/modern/useInfiniteQuery.js +17 -0
  206. package/build/modern/useInfiniteQuery.js.map +1 -0
  207. package/build/modern/useIsFetching.cjs +56 -0
  208. package/build/modern/useIsFetching.cjs.map +1 -0
  209. package/build/modern/useIsFetching.d.cts +5 -0
  210. package/build/modern/useIsFetching.d.ts +5 -0
  211. package/build/modern/useIsFetching.js +22 -0
  212. package/build/modern/useIsFetching.js.map +1 -0
  213. package/build/modern/useMutation.cjs +77 -0
  214. package/build/modern/useMutation.cjs.map +1 -0
  215. package/build/modern/useMutation.d.cts +6 -0
  216. package/build/modern/useMutation.d.ts +6 -0
  217. package/build/modern/useMutation.js +43 -0
  218. package/build/modern/useMutation.js.map +1 -0
  219. package/build/modern/useMutationState.cjs +88 -0
  220. package/build/modern/useMutationState.cjs.map +1 -0
  221. package/build/modern/useMutationState.d.cts +10 -0
  222. package/build/modern/useMutationState.d.ts +10 -0
  223. package/build/modern/useMutationState.js +53 -0
  224. package/build/modern/useMutationState.js.map +1 -0
  225. package/build/modern/useQueries.cjs +125 -0
  226. package/build/modern/useQueries.cjs.map +1 -0
  227. package/build/modern/useQueries.d.cts +53 -0
  228. package/build/modern/useQueries.d.ts +53 -0
  229. package/build/modern/useQueries.js +100 -0
  230. package/build/modern/useQueries.js.map +1 -0
  231. package/build/modern/useQuery.cjs +36 -0
  232. package/build/modern/useQuery.cjs.map +1 -0
  233. package/build/modern/useQuery.d.cts +8 -0
  234. package/build/modern/useQuery.d.ts +8 -0
  235. package/build/modern/useQuery.js +12 -0
  236. package/build/modern/useQuery.js.map +1 -0
  237. package/build/modern/useSuspenseInfiniteQuery.cjs +46 -0
  238. package/build/modern/useSuspenseInfiniteQuery.cjs.map +1 -0
  239. package/build/modern/useSuspenseInfiniteQuery.d.cts +6 -0
  240. package/build/modern/useSuspenseInfiniteQuery.d.ts +6 -0
  241. package/build/modern/useSuspenseInfiniteQuery.js +22 -0
  242. package/build/modern/useSuspenseInfiniteQuery.js.map +1 -0
  243. package/build/modern/useSuspenseQuery.cjs +45 -0
  244. package/build/modern/useSuspenseQuery.cjs.map +1 -0
  245. package/build/modern/useSuspenseQuery.d.cts +6 -0
  246. package/build/modern/useSuspenseQuery.d.ts +6 -0
  247. package/build/modern/useSuspenseQuery.js +21 -0
  248. package/build/modern/useSuspenseQuery.js.map +1 -0
  249. package/build/modern/utils.cjs +36 -0
  250. package/build/modern/utils.cjs.map +1 -0
  251. package/build/modern/utils.d.cts +3 -0
  252. package/build/modern/utils.d.ts +3 -0
  253. package/build/modern/utils.js +11 -0
  254. package/build/modern/utils.js.map +1 -0
  255. package/package.json +29 -17
  256. package/src/HydrationBoundary.tsx +1 -1
  257. package/src/__tests__/HydrationBoundary.test.tsx +5 -5
  258. package/src/__tests__/QueryClientProvider.test.tsx +2 -2
  259. package/src/__tests__/QueryResetErrorBoundary.test.tsx +753 -621
  260. package/src/__tests__/ssr-hydration.test.tsx +4 -4
  261. package/src/__tests__/ssr.test.tsx +2 -2
  262. package/src/__tests__/suspense.test.tsx +8 -90
  263. package/src/__tests__/useInfiniteQuery.test.tsx +36 -74
  264. package/src/__tests__/useInfiniteQuery.type.test.tsx +2 -29
  265. package/src/__tests__/useIsFetching.test.tsx +1 -1
  266. package/src/__tests__/useMutation.test.tsx +22 -23
  267. package/src/__tests__/useMutationState.test.tsx +23 -0
  268. package/src/__tests__/useQueries.test.tsx +214 -91
  269. package/src/__tests__/useQuery.test.tsx +691 -551
  270. package/src/__tests__/useQuery.types.test.tsx +22 -1
  271. package/src/__tests__/utils.tsx +11 -5
  272. package/src/errorBoundaryUtils.ts +7 -7
  273. package/src/index.ts +4 -0
  274. package/src/infiniteQueryOptions.ts +93 -0
  275. package/src/queryOptions.ts +42 -0
  276. package/src/suspense.ts +9 -15
  277. package/src/types.ts +50 -8
  278. package/src/useBaseQuery.ts +14 -26
  279. package/src/useInfiniteQuery.ts +50 -7
  280. package/src/useIsFetching.ts +1 -1
  281. package/src/useMutation.ts +4 -4
  282. package/src/useMutationState.ts +8 -8
  283. package/src/useQueries.ts +53 -36
  284. package/src/useQuery.ts +6 -21
  285. package/src/useSuspenseInfiniteQuery.ts +47 -0
  286. package/src/useSuspenseQuery.ts +26 -0
  287. package/build/lib/HydrationBoundary.d.ts +0 -9
  288. package/build/lib/HydrationBoundary.esm.js +0 -29
  289. package/build/lib/HydrationBoundary.esm.js.map +0 -1
  290. package/build/lib/HydrationBoundary.js +0 -50
  291. package/build/lib/HydrationBoundary.js.map +0 -1
  292. package/build/lib/HydrationBoundary.mjs +0 -29
  293. package/build/lib/HydrationBoundary.mjs.map +0 -1
  294. package/build/lib/QueryClientProvider.d.ts +0 -9
  295. package/build/lib/QueryClientProvider.esm.js +0 -31
  296. package/build/lib/QueryClientProvider.esm.js.map +0 -1
  297. package/build/lib/QueryClientProvider.js +0 -54
  298. package/build/lib/QueryClientProvider.js.map +0 -1
  299. package/build/lib/QueryClientProvider.mjs +0 -31
  300. package/build/lib/QueryClientProvider.mjs.map +0 -1
  301. package/build/lib/QueryErrorResetBoundary.d.ts +0 -11
  302. package/build/lib/QueryErrorResetBoundary.esm.js +0 -38
  303. package/build/lib/QueryErrorResetBoundary.esm.js.map +0 -1
  304. package/build/lib/QueryErrorResetBoundary.js +0 -60
  305. package/build/lib/QueryErrorResetBoundary.js.map +0 -1
  306. package/build/lib/QueryErrorResetBoundary.mjs +0 -38
  307. package/build/lib/QueryErrorResetBoundary.mjs.map +0 -1
  308. package/build/lib/__tests__/HydrationBoundary.test.d.ts +0 -1
  309. package/build/lib/__tests__/QueryClientProvider.test.d.ts +0 -1
  310. package/build/lib/__tests__/QueryResetErrorBoundary.test.d.ts +0 -6
  311. package/build/lib/__tests__/ssr-hydration.test.d.ts +0 -1
  312. package/build/lib/__tests__/ssr.test.d.ts +0 -1
  313. package/build/lib/__tests__/suspense.test.d.ts +0 -1
  314. package/build/lib/__tests__/useInfiniteQuery.test.d.ts +0 -1
  315. package/build/lib/__tests__/useInfiniteQuery.type.test.d.ts +0 -1
  316. package/build/lib/__tests__/useIsFetching.test.d.ts +0 -1
  317. package/build/lib/__tests__/useMutation.test.d.ts +0 -1
  318. package/build/lib/__tests__/useMutationState.test.d.ts +0 -1
  319. package/build/lib/__tests__/useQueries.test.d.ts +0 -1
  320. package/build/lib/__tests__/useQuery.test.d.ts +0 -1
  321. package/build/lib/__tests__/useQuery.types.test.d.ts +0 -1
  322. package/build/lib/__tests__/utils.d.ts +0 -28
  323. package/build/lib/errorBoundaryUtils.d.ts +0 -10
  324. package/build/lib/errorBoundaryUtils.esm.js +0 -28
  325. package/build/lib/errorBoundaryUtils.esm.js.map +0 -1
  326. package/build/lib/errorBoundaryUtils.js +0 -51
  327. package/build/lib/errorBoundaryUtils.js.map +0 -1
  328. package/build/lib/errorBoundaryUtils.mjs +0 -28
  329. package/build/lib/errorBoundaryUtils.mjs.map +0 -1
  330. package/build/lib/index.d.ts +0 -16
  331. package/build/lib/index.esm.js +0 -12
  332. package/build/lib/index.esm.js.map +0 -1
  333. package/build/lib/index.js +0 -38
  334. package/build/lib/index.js.map +0 -1
  335. package/build/lib/index.mjs +0 -12
  336. package/build/lib/index.mjs.map +0 -1
  337. package/build/lib/isRestoring.d.ts +0 -3
  338. package/build/lib/isRestoring.esm.js +0 -9
  339. package/build/lib/isRestoring.esm.js.map +0 -1
  340. package/build/lib/isRestoring.js +0 -31
  341. package/build/lib/isRestoring.js.map +0 -1
  342. package/build/lib/isRestoring.mjs +0 -9
  343. package/build/lib/isRestoring.mjs.map +0 -1
  344. package/build/lib/suspense.d.ts +0 -9
  345. package/build/lib/suspense.esm.js +0 -24
  346. package/build/lib/suspense.esm.js.map +0 -1
  347. package/build/lib/suspense.js +0 -29
  348. package/build/lib/suspense.js.map +0 -1
  349. package/build/lib/suspense.mjs +0 -24
  350. package/build/lib/suspense.mjs.map +0 -1
  351. package/build/lib/types.d.ts +0 -26
  352. package/build/lib/useBaseQuery.d.ts +0 -3
  353. package/build/lib/useBaseQuery.esm.js +0 -63
  354. package/build/lib/useBaseQuery.esm.js.map +0 -1
  355. package/build/lib/useBaseQuery.js +0 -84
  356. package/build/lib/useBaseQuery.js.map +0 -1
  357. package/build/lib/useBaseQuery.mjs +0 -63
  358. package/build/lib/useBaseQuery.mjs.map +0 -1
  359. package/build/lib/useInfiniteQuery.d.ts +0 -3
  360. package/build/lib/useInfiniteQuery.esm.js +0 -13
  361. package/build/lib/useInfiniteQuery.esm.js.map +0 -1
  362. package/build/lib/useInfiniteQuery.js +0 -15
  363. package/build/lib/useInfiniteQuery.js.map +0 -1
  364. package/build/lib/useInfiniteQuery.mjs +0 -13
  365. package/build/lib/useInfiniteQuery.mjs.map +0 -1
  366. package/build/lib/useIsFetching.d.ts +0 -2
  367. package/build/lib/useIsFetching.esm.js +0 -13
  368. package/build/lib/useIsFetching.esm.js.map +0 -1
  369. package/build/lib/useIsFetching.js +0 -34
  370. package/build/lib/useIsFetching.js.map +0 -1
  371. package/build/lib/useIsFetching.mjs +0 -13
  372. package/build/lib/useIsFetching.mjs.map +0 -1
  373. package/build/lib/useMutation.d.ts +0 -3
  374. package/build/lib/useMutation.esm.js +0 -33
  375. package/build/lib/useMutation.esm.js.map +0 -1
  376. package/build/lib/useMutation.js +0 -54
  377. package/build/lib/useMutation.js.map +0 -1
  378. package/build/lib/useMutation.mjs +0 -33
  379. package/build/lib/useMutation.mjs.map +0 -1
  380. package/build/lib/useMutationState.d.ts +0 -8
  381. package/build/lib/useMutationState.esm.js +0 -38
  382. package/build/lib/useMutationState.esm.js.map +0 -1
  383. package/build/lib/useMutationState.js +0 -60
  384. package/build/lib/useMutationState.js.map +0 -1
  385. package/build/lib/useMutationState.mjs +0 -38
  386. package/build/lib/useMutationState.mjs.map +0 -1
  387. package/build/lib/useQueries.esm.js +0 -73
  388. package/build/lib/useQueries.esm.js.map +0 -1
  389. package/build/lib/useQueries.js +0 -94
  390. package/build/lib/useQueries.js.map +0 -1
  391. package/build/lib/useQueries.mjs +0 -70
  392. package/build/lib/useQueries.mjs.map +0 -1
  393. package/build/lib/useQuery.d.ts +0 -11
  394. package/build/lib/useQuery.esm.js +0 -12
  395. package/build/lib/useQuery.esm.js.map +0 -1
  396. package/build/lib/useQuery.js +0 -14
  397. package/build/lib/useQuery.js.map +0 -1
  398. package/build/lib/useQuery.mjs +0 -12
  399. package/build/lib/useQuery.mjs.map +0 -1
  400. package/build/lib/utils.d.ts +0 -1
  401. package/build/lib/utils.esm.js +0 -10
  402. package/build/lib/utils.esm.js.map +0 -1
  403. package/build/lib/utils.js +0 -12
  404. package/build/lib/utils.js.map +0 -1
  405. package/build/lib/utils.mjs +0 -10
  406. package/build/lib/utils.mjs.map +0 -1
  407. package/build/umd/index.development.js +0 -3040
  408. package/build/umd/index.development.js.map +0 -1
  409. package/build/umd/index.production.js +0 -2
  410. package/build/umd/index.production.js.map +0 -1
  411. /package/src/{isRestoring.tsx → isRestoring.ts} +0 -0
@@ -1,11 +1,14 @@
1
1
  import { act, fireEvent, render, waitFor } from '@testing-library/react'
2
2
  import '@testing-library/jest-dom'
3
3
  import * as React from 'react'
4
+ import { ErrorBoundary } from 'react-error-boundary'
5
+ import { vi } from 'vitest'
6
+ import { QueryCache, keepPreviousData, useQuery } from '..'
4
7
  import {
5
8
  Blink,
6
9
  createQueryClient,
7
10
  expectType,
8
- mockNavigatorOnLine,
11
+ mockOnlineManagerIsOnline,
9
12
  mockVisibilityState,
10
13
  queryKey,
11
14
  renderWithClient,
@@ -15,13 +18,9 @@ import {
15
18
  import type {
16
19
  DefinedUseQueryResult,
17
20
  QueryFunction,
18
- QueryFunctionContext,
19
21
  UseQueryOptions,
20
22
  UseQueryResult,
21
23
  } from '..'
22
- import { QueryCache, useQuery, keepPreviousData } from '..'
23
- import { ErrorBoundary } from 'react-error-boundary'
24
- import { vi } from 'vitest'
25
24
  import type { Mock } from 'vitest'
26
25
 
27
26
  describe('useQuery', () => {
@@ -64,23 +63,17 @@ describe('useQuery', () => {
64
63
  useQuery({
65
64
  queryKey: [key],
66
65
  queryFn: async () => true,
67
- onSuccess: (data) => expectType<boolean>(data),
68
- onSettled: (data) => expectType<boolean | undefined>(data),
69
66
  })
70
67
 
71
68
  // it should be possible to specify a union type as result type
72
69
  const unionTypeSync = useQuery({
73
70
  queryKey: key,
74
71
  queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'),
75
-
76
- onSuccess: (data) => expectType<'a' | 'b'>(data),
77
72
  })
78
73
  expectType<'a' | 'b' | undefined>(unionTypeSync.data)
79
74
  const unionTypeAsync = useQuery<'a' | 'b'>({
80
75
  queryKey: key,
81
76
  queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'),
82
-
83
- onSuccess: (data) => expectType<'a' | 'b'>(data),
84
77
  })
85
78
  expectType<'a' | 'b' | undefined>(unionTypeAsync.data)
86
79
 
@@ -450,255 +443,6 @@ describe('useQuery', () => {
450
443
  })
451
444
  })
452
445
 
453
- it('should call onSuccess after a query has been fetched', async () => {
454
- const key = queryKey()
455
- const states: UseQueryResult<string>[] = []
456
- const onSuccess = vi.fn()
457
-
458
- function Page() {
459
- const state = useQuery({
460
- queryKey: key,
461
- queryFn: async () => {
462
- await sleep(10)
463
- return 'data'
464
- },
465
- onSuccess,
466
- })
467
- states.push(state)
468
- return <div>data: {state.data}</div>
469
- }
470
-
471
- const rendered = renderWithClient(queryClient, <Page />)
472
-
473
- await rendered.findByText('data: data')
474
- expect(states.length).toBe(2)
475
- expect(onSuccess).toHaveBeenCalledTimes(1)
476
- expect(onSuccess).toHaveBeenCalledWith('data')
477
- })
478
-
479
- it('should call onSuccess after a query has been refetched', async () => {
480
- const key = queryKey()
481
- const states: UseQueryResult<string>[] = []
482
- const onSuccess = vi.fn()
483
- let count = 0
484
-
485
- function Page() {
486
- const state = useQuery({
487
- queryKey: key,
488
- queryFn: async () => {
489
- count++
490
- await sleep(10)
491
- return 'data' + count
492
- },
493
- onSuccess,
494
- })
495
-
496
- states.push(state)
497
-
498
- return (
499
- <div>
500
- <div>data: {state.data}</div>
501
- <button onClick={() => state.refetch()}>refetch</button>
502
- </div>
503
- )
504
- }
505
-
506
- const rendered = renderWithClient(queryClient, <Page />)
507
-
508
- await rendered.findByText('data: data1')
509
- fireEvent.click(rendered.getByRole('button', { name: /refetch/i }))
510
- await rendered.findByText('data: data2')
511
-
512
- expect(states.length).toBe(3) //pending, success, success after refetch
513
- expect(count).toBe(2)
514
- expect(onSuccess).toHaveBeenCalledTimes(2)
515
- })
516
-
517
- it('should call onSuccess after a disabled query has been fetched', async () => {
518
- const key = queryKey()
519
- const states: UseQueryResult<string>[] = []
520
- const onSuccess = vi.fn()
521
-
522
- function Page() {
523
- const state = useQuery({
524
- queryKey: key,
525
- queryFn: () => 'data',
526
- enabled: false,
527
- onSuccess,
528
- })
529
-
530
- states.push(state)
531
-
532
- return (
533
- <div>
534
- <div>isSuccess: {state.isSuccess ? 'true' : 'false'}</div>
535
- <button onClick={() => state.refetch()}>refetch</button>
536
- </div>
537
- )
538
- }
539
-
540
- const rendered = renderWithClient(queryClient, <Page />)
541
-
542
- await waitFor(() => {
543
- rendered.getByText('isSuccess: false')
544
- })
545
-
546
- fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
547
-
548
- await waitFor(() => {
549
- rendered.getByText('isSuccess: true')
550
- })
551
-
552
- expect(onSuccess).toHaveBeenCalledTimes(1)
553
- expect(onSuccess).toHaveBeenCalledWith('data')
554
- })
555
-
556
- it('should not call onSuccess if a component has unmounted', async () => {
557
- const key = queryKey()
558
- const states: UseQueryResult<string>[] = []
559
- const onSuccess = vi.fn()
560
-
561
- function Page() {
562
- const [show, setShow] = React.useState(true)
563
-
564
- React.useEffect(() => {
565
- setShow(false)
566
- }, [setShow])
567
-
568
- return show ? <Component /> : null
569
- }
570
-
571
- function Component() {
572
- const state = useQuery({
573
- queryKey: key,
574
- queryFn: async () => {
575
- await sleep(10)
576
- return 'data'
577
- },
578
- onSuccess,
579
- })
580
- states.push(state)
581
- return null
582
- }
583
-
584
- renderWithClient(queryClient, <Page />)
585
-
586
- await sleep(50)
587
- expect(states.length).toBe(1)
588
- expect(onSuccess).toHaveBeenCalledTimes(0)
589
- })
590
-
591
- it('should call onError after a query has been fetched with an error', async () => {
592
- const key = queryKey()
593
- const states: UseQueryResult<unknown>[] = []
594
- const onError = vi.fn()
595
-
596
- function Page() {
597
- const state = useQuery<unknown>({
598
- queryKey: key,
599
- queryFn: () => Promise.reject(new Error('error')),
600
- retry: false,
601
- onError,
602
- })
603
- states.push(state)
604
- return null
605
- }
606
-
607
- renderWithClient(queryClient, <Page />)
608
-
609
- await sleep(10)
610
- expect(states.length).toBe(2)
611
- expect(onError).toHaveBeenCalledTimes(1)
612
- expect(onError).toHaveBeenCalledWith(new Error('error'))
613
- })
614
-
615
- it('should not call onError when receiving a CancelledError', async () => {
616
- const key = queryKey()
617
- const onError = vi.fn()
618
-
619
- function Page() {
620
- const { status, fetchStatus } = useQuery({
621
- queryKey: key,
622
- queryFn: async () => {
623
- await sleep(10)
624
- return 23
625
- },
626
-
627
- onError,
628
- })
629
- return (
630
- <span>
631
- status: {status}, fetchStatus: {fetchStatus}
632
- </span>
633
- )
634
- }
635
-
636
- const rendered = renderWithClient(queryClient, <Page />)
637
-
638
- rendered.getByText('status: pending, fetchStatus: fetching')
639
-
640
- await queryClient.cancelQueries({ queryKey: key })
641
- // query cancellation will reset the query to it's initial state
642
- await waitFor(() =>
643
- rendered.getByText('status: pending, fetchStatus: idle'),
644
- )
645
- expect(onError).not.toHaveBeenCalled()
646
- })
647
-
648
- it('should call onSettled after a query has been fetched', async () => {
649
- const key = queryKey()
650
- const states: UseQueryResult<string>[] = []
651
- const onSettled = vi.fn()
652
-
653
- function Page() {
654
- const state = useQuery({
655
- queryKey: key,
656
- queryFn: () => 'data',
657
- onSettled,
658
- })
659
- states.push(state)
660
-
661
- return <div>data: {state.data}</div>
662
- }
663
-
664
- const rendered = renderWithClient(queryClient, <Page />)
665
-
666
- await waitFor(() => {
667
- rendered.getByText('data: data')
668
- })
669
-
670
- expect(states.length).toBe(2)
671
- expect(onSettled).toHaveBeenCalledTimes(1)
672
- expect(onSettled).toHaveBeenCalledWith('data', null)
673
- })
674
-
675
- it('should call onSettled after a query has been fetched with an error', async () => {
676
- const key = queryKey()
677
- const onSettled = vi.fn()
678
- const error = new Error('error')
679
-
680
- function Page() {
681
- const state = useQuery({
682
- queryKey: key,
683
- queryFn: async () => {
684
- await sleep(10)
685
- return Promise.reject(error)
686
- },
687
- retry: false,
688
- onSettled,
689
- })
690
- return <div>status: {state.status}</div>
691
- }
692
-
693
- const rendered = renderWithClient(queryClient, <Page />)
694
-
695
- await waitFor(() => {
696
- rendered.getByText('status: error')
697
- })
698
- expect(onSettled).toHaveBeenCalledTimes(1)
699
- expect(onSettled).toHaveBeenCalledWith(undefined, error)
700
- })
701
-
702
446
  it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => {
703
447
  const key = queryKey()
704
448
  let fetchCount = 0
@@ -948,17 +692,15 @@ describe('useQuery', () => {
948
692
  // required to make sure no additional renders are happening after data is successfully fetched for the second time
949
693
  await sleep(100)
950
694
 
951
- expect(states.length).toBe(5)
695
+ expect(states.length).toBe(4)
952
696
  // First load
953
697
  expect(states[0]).toMatchObject({ isPending: true, isSuccess: false })
954
698
  // First success
955
699
  expect(states[1]).toMatchObject({ isPending: false, isSuccess: true })
956
700
  // Remove
957
701
  expect(states[2]).toMatchObject({ isPending: true, isSuccess: false })
958
- // Hook state update
959
- expect(states[3]).toMatchObject({ isPending: true, isSuccess: false })
960
702
  // Second success
961
- expect(states[4]).toMatchObject({ isPending: false, isSuccess: true })
703
+ expect(states[3]).toMatchObject({ isPending: false, isSuccess: true })
962
704
  })
963
705
 
964
706
  it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => {
@@ -1060,48 +802,6 @@ describe('useQuery', () => {
1060
802
  expect(states[1]).toMatchObject({ data: 'test' })
1061
803
  })
1062
804
 
1063
- it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
1064
- const key = queryKey()
1065
- const states: UseQueryResult<string>[] = []
1066
-
1067
- function Page() {
1068
- const state = useQuery({
1069
- queryKey: key,
1070
- queryFn: () => ({ name: 'test' }),
1071
- select: (data) => data.name,
1072
- notifyOnChangeProps: ['data'],
1073
- })
1074
-
1075
- states.push(state)
1076
-
1077
- return (
1078
- <div>
1079
- <div>{state.data}</div>
1080
- <button onClick={() => state.refetch()}>refetch</button>
1081
- </div>
1082
- )
1083
- }
1084
-
1085
- const rendered = renderWithClient(queryClient, <Page />)
1086
-
1087
- await waitFor(() => {
1088
- rendered.getByText('test')
1089
- })
1090
-
1091
- fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
1092
-
1093
- await waitFor(() => {
1094
- rendered.getByText('test')
1095
- })
1096
-
1097
- expect(states[0]).toMatchObject({ data: undefined })
1098
- expect(states[1]).toMatchObject({ data: 'test' })
1099
-
1100
- // make sure no additional renders happen
1101
- await sleep(50)
1102
- expect(states.length).toBe(2)
1103
- })
1104
-
1105
805
  it('should throw an error when a selector throws', async () => {
1106
806
  const key = queryKey()
1107
807
  const states: UseQueryResult<string>[] = []
@@ -1728,21 +1428,24 @@ describe('useQuery', () => {
1728
1428
  })
1729
1429
  })
1730
1430
 
1731
- it('should transition to error state when placeholderData is set', async () => {
1431
+ it('should keep the previous data when placeholderData is set and select fn transform is used', async () => {
1732
1432
  const key = queryKey()
1733
1433
  const states: UseQueryResult<number>[] = []
1734
1434
 
1735
- function Page({ count }: { count: number }) {
1736
- const state = useQuery<number, Error>({
1435
+ function Page() {
1436
+ const [count, setCount] = React.useState(0)
1437
+
1438
+ const state = useQuery({
1737
1439
  queryKey: [key, count],
1738
1440
  queryFn: async () => {
1739
1441
  await sleep(10)
1740
- if (count === 2) {
1741
- throw new Error('Error test')
1442
+ return {
1443
+ count,
1742
1444
  }
1743
- return Promise.resolve(count)
1744
1445
  },
1745
- retry: false,
1446
+ select(data) {
1447
+ return data.count
1448
+ },
1746
1449
  placeholderData: keepPreviousData,
1747
1450
  })
1748
1451
 
@@ -1750,71 +1453,259 @@ describe('useQuery', () => {
1750
1453
 
1751
1454
  return (
1752
1455
  <div>
1753
- <h1>data: {state.data}</h1>
1754
- <h2>error: {state.error?.message}</h2>
1755
- <p>placeholder data: {state.isPlaceholderData}</p>
1456
+ <div>data: {state.data}</div>
1457
+ <button onClick={() => setCount(1)}>setCount</button>
1756
1458
  </div>
1757
1459
  )
1758
1460
  }
1759
1461
 
1760
- const rendered = renderWithClient(queryClient, <Page count={0} />)
1462
+ const rendered = renderWithClient(queryClient, <Page />)
1463
+
1761
1464
  await waitFor(() => rendered.getByText('data: 0'))
1762
- act(() => rendered.rerender(<Page count={1} />))
1763
- await waitFor(() => rendered.getByText('data: 1'))
1764
- act(() => rendered.rerender(<Page count={2} />))
1765
- await waitFor(() => rendered.getByText('error: Error test'))
1766
1465
 
1767
- await waitFor(() => expect(states.length).toBe(8))
1768
- // Initial
1466
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1467
+
1468
+ await waitFor(() => rendered.getByText('data: 1'))
1469
+
1470
+ // Initial
1769
1471
  expect(states[0]).toMatchObject({
1770
1472
  data: undefined,
1771
1473
  isFetching: true,
1772
- status: 'pending',
1773
- error: null,
1474
+ isSuccess: false,
1774
1475
  isPlaceholderData: false,
1775
1476
  })
1776
1477
  // Fetched
1777
1478
  expect(states[1]).toMatchObject({
1778
1479
  data: 0,
1779
1480
  isFetching: false,
1780
- status: 'success',
1781
- error: null,
1481
+ isSuccess: true,
1782
1482
  isPlaceholderData: false,
1783
1483
  })
1784
- // rerender Page 1
1484
+ // Set state
1785
1485
  expect(states[2]).toMatchObject({
1786
1486
  data: 0,
1787
1487
  isFetching: true,
1788
- status: 'success',
1789
- error: null,
1488
+ isSuccess: true,
1489
+ isPlaceholderData: true,
1490
+ })
1491
+ // New data
1492
+ expect(states[3]).toMatchObject({
1493
+ data: 1,
1494
+ isFetching: false,
1495
+ isSuccess: true,
1496
+ isPlaceholderData: false,
1497
+ })
1498
+ })
1499
+
1500
+ it('should keep the previous queryKey (from prevQuery) between multiple pending queries when placeholderData is set and select fn transform is used', async () => {
1501
+ const keys: Array<readonly unknown[] | null> = []
1502
+ const key = queryKey()
1503
+ const states: UseQueryResult<number>[] = []
1504
+
1505
+ function Page() {
1506
+ const [count, setCount] = React.useState(0)
1507
+
1508
+ const state = useQuery({
1509
+ queryKey: [key, count],
1510
+ queryFn: async () => {
1511
+ await sleep(10)
1512
+ return {
1513
+ count,
1514
+ }
1515
+ },
1516
+ select(data) {
1517
+ return data.count
1518
+ },
1519
+ placeholderData: (prevData, prevQuery) => {
1520
+ if (prevQuery) {
1521
+ keys.push(prevQuery.queryKey)
1522
+ }
1523
+ return prevData
1524
+ },
1525
+ })
1526
+
1527
+ states.push(state)
1528
+
1529
+ return (
1530
+ <div>
1531
+ <div>data: {state.data}</div>
1532
+ <button onClick={() => setCount((prev) => prev + 1)}>setCount</button>
1533
+ </div>
1534
+ )
1535
+ }
1536
+
1537
+ const rendered = renderWithClient(queryClient, <Page />)
1538
+
1539
+ await waitFor(() => rendered.getByText('data: 0'))
1540
+
1541
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1542
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1543
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1544
+
1545
+ await waitFor(() => rendered.getByText('data: 3'))
1546
+
1547
+ const allPreviousKeysAreTheFirstQueryKey = keys.every(
1548
+ (k) => JSON.stringify(k) === JSON.stringify([key, 0]),
1549
+ )
1550
+
1551
+ expect(allPreviousKeysAreTheFirstQueryKey).toBe(true)
1552
+ })
1553
+
1554
+ it('should show placeholderData between multiple pending queries when select fn transform is used', async () => {
1555
+ const key = queryKey()
1556
+ const states: UseQueryResult<number>[] = []
1557
+
1558
+ function Page() {
1559
+ const [count, setCount] = React.useState(0)
1560
+
1561
+ const state = useQuery({
1562
+ queryKey: [key, count],
1563
+ queryFn: async () => {
1564
+ await sleep(10)
1565
+ return {
1566
+ count,
1567
+ }
1568
+ },
1569
+ select(data) {
1570
+ return data.count
1571
+ },
1572
+ placeholderData: keepPreviousData,
1573
+ })
1574
+
1575
+ states.push(state)
1576
+
1577
+ return (
1578
+ <div>
1579
+ <div>data: {state.data}</div>
1580
+ <button onClick={() => setCount((prev) => prev + 1)}>setCount</button>
1581
+ </div>
1582
+ )
1583
+ }
1584
+
1585
+ const rendered = renderWithClient(queryClient, <Page />)
1586
+
1587
+ await waitFor(() => rendered.getByText('data: 0'))
1588
+
1589
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1590
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1591
+ fireEvent.click(rendered.getByRole('button', { name: 'setCount' }))
1592
+
1593
+ await waitFor(() => rendered.getByText('data: 3'))
1594
+ // Initial
1595
+ expect(states[0]).toMatchObject({
1596
+ data: undefined,
1597
+ isFetching: true,
1598
+ isSuccess: false,
1599
+ isPlaceholderData: false,
1600
+ })
1601
+ // Fetched
1602
+ expect(states[1]).toMatchObject({
1603
+ data: 0,
1604
+ isFetching: false,
1605
+ isSuccess: true,
1606
+ isPlaceholderData: false,
1607
+ })
1608
+ // Set state -> count = 1
1609
+ expect(states[2]).toMatchObject({
1610
+ data: 0,
1611
+ isFetching: true,
1612
+ isSuccess: true,
1790
1613
  isPlaceholderData: true,
1791
1614
  })
1792
- // Hook state update
1615
+ // Set state -> count = 2
1793
1616
  expect(states[3]).toMatchObject({
1794
1617
  data: 0,
1795
1618
  isFetching: true,
1796
- status: 'success',
1797
- error: null,
1619
+ isSuccess: true,
1798
1620
  isPlaceholderData: true,
1799
1621
  })
1800
- // New data
1622
+ // Set state -> count = 3
1801
1623
  expect(states[4]).toMatchObject({
1802
- data: 1,
1624
+ data: 0,
1625
+ isFetching: true,
1626
+ isSuccess: true,
1627
+ isPlaceholderData: true,
1628
+ })
1629
+ // New data
1630
+ expect(states[5]).toMatchObject({
1631
+ data: 3,
1632
+ isFetching: false,
1633
+ isSuccess: true,
1634
+ isPlaceholderData: false,
1635
+ })
1636
+ })
1637
+
1638
+ it('should transition to error state when placeholderData is set', async () => {
1639
+ const key = queryKey()
1640
+ const states: UseQueryResult<number>[] = []
1641
+
1642
+ function Page({ count }: { count: number }) {
1643
+ const state = useQuery<number, Error>({
1644
+ queryKey: [key, count],
1645
+ queryFn: async () => {
1646
+ await sleep(10)
1647
+ if (count === 2) {
1648
+ throw new Error('Error test')
1649
+ }
1650
+ return Promise.resolve(count)
1651
+ },
1652
+ retry: false,
1653
+ placeholderData: keepPreviousData,
1654
+ })
1655
+
1656
+ states.push(state)
1657
+
1658
+ return (
1659
+ <div>
1660
+ <h1>data: {state.data}</h1>
1661
+ <h2>error: {state.error?.message}</h2>
1662
+ <p>placeholder data: {state.isPlaceholderData}</p>
1663
+ </div>
1664
+ )
1665
+ }
1666
+
1667
+ const rendered = renderWithClient(queryClient, <Page count={0} />)
1668
+ await waitFor(() => rendered.getByText('data: 0'))
1669
+ act(() => rendered.rerender(<Page count={1} />))
1670
+ await waitFor(() => rendered.getByText('data: 1'))
1671
+ act(() => rendered.rerender(<Page count={2} />))
1672
+ await waitFor(() => rendered.getByText('error: Error test'))
1673
+
1674
+ await waitFor(() => expect(states.length).toBe(6))
1675
+ // Initial
1676
+ expect(states[0]).toMatchObject({
1677
+ data: undefined,
1678
+ isFetching: true,
1679
+ status: 'pending',
1680
+ error: null,
1681
+ isPlaceholderData: false,
1682
+ })
1683
+ // Fetched
1684
+ expect(states[1]).toMatchObject({
1685
+ data: 0,
1803
1686
  isFetching: false,
1804
1687
  status: 'success',
1805
1688
  error: null,
1806
1689
  isPlaceholderData: false,
1807
1690
  })
1808
- // rerender Page 2
1809
- expect(states[5]).toMatchObject({
1810
- data: 1,
1691
+ // rerender Page 1
1692
+ expect(states[2]).toMatchObject({
1693
+ data: 0,
1811
1694
  isFetching: true,
1812
1695
  status: 'success',
1813
1696
  error: null,
1814
1697
  isPlaceholderData: true,
1815
1698
  })
1816
- // Hook state update again
1817
- expect(states[6]).toMatchObject({
1699
+ // New data
1700
+ expect(states[3]).toMatchObject({
1701
+ data: 1,
1702
+ isFetching: false,
1703
+ status: 'success',
1704
+ error: null,
1705
+ isPlaceholderData: false,
1706
+ })
1707
+ // rerender Page 2
1708
+ expect(states[4]).toMatchObject({
1818
1709
  data: 1,
1819
1710
  isFetching: true,
1820
1711
  status: 'success',
@@ -1822,13 +1713,13 @@ describe('useQuery', () => {
1822
1713
  isPlaceholderData: true,
1823
1714
  })
1824
1715
  // Error
1825
- expect(states[7]).toMatchObject({
1716
+ expect(states[5]).toMatchObject({
1826
1717
  data: undefined,
1827
1718
  isFetching: false,
1828
1719
  status: 'error',
1829
1720
  isPlaceholderData: false,
1830
1721
  })
1831
- expect(states[7]?.error).toHaveProperty('message', 'Error test')
1722
+ expect(states[5]!.error).toHaveProperty('message', 'Error test')
1832
1723
  })
1833
1724
 
1834
1725
  it('should not show initial data from next query if placeholderData is set', async () => {
@@ -1873,7 +1764,7 @@ describe('useQuery', () => {
1873
1764
  rendered.getByText('data: 1, count: 1, isFetching: false'),
1874
1765
  )
1875
1766
 
1876
- expect(states.length).toBe(5)
1767
+ expect(states.length).toBe(4)
1877
1768
 
1878
1769
  // Initial
1879
1770
  expect(states[0]).toMatchObject({
@@ -1896,15 +1787,8 @@ describe('useQuery', () => {
1896
1787
  isSuccess: true,
1897
1788
  isPlaceholderData: false,
1898
1789
  })
1899
- // Hook state update
1900
- expect(states[3]).toMatchObject({
1901
- data: 99,
1902
- isFetching: true,
1903
- isSuccess: true,
1904
- isPlaceholderData: false,
1905
- })
1906
1790
  // New data
1907
- expect(states[4]).toMatchObject({
1791
+ expect(states[3]).toMatchObject({
1908
1792
  data: 1,
1909
1793
  isFetching: false,
1910
1794
  isSuccess: true,
@@ -2276,67 +2160,271 @@ describe('useQuery', () => {
2276
2160
 
2277
2161
  renderWithClient(queryClient, <Page />)
2278
2162
 
2279
- await sleep(100)
2163
+ await sleep(100)
2164
+
2165
+ expect(states.length).toBe(3)
2166
+ expect(states[0]).toMatchObject({ isStale: true })
2167
+ expect(states[1]).toMatchObject({ isStale: false })
2168
+ expect(states[2]).toMatchObject({ isStale: true })
2169
+ })
2170
+
2171
+ describe('notifyOnChangeProps', () => {
2172
+ it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
2173
+ const key = queryKey()
2174
+ const states: UseQueryResult<string>[] = []
2175
+
2176
+ function Page() {
2177
+ const state = useQuery({
2178
+ queryKey: key,
2179
+ queryFn: () => ({ name: 'test' }),
2180
+ select: (data) => data.name,
2181
+ notifyOnChangeProps: ['data'],
2182
+ })
2183
+
2184
+ states.push(state)
2185
+
2186
+ return (
2187
+ <div>
2188
+ <div>{state.data}</div>
2189
+ <button onClick={() => state.refetch()}>refetch</button>
2190
+ </div>
2191
+ )
2192
+ }
2193
+
2194
+ const rendered = renderWithClient(queryClient, <Page />)
2195
+
2196
+ await waitFor(() => {
2197
+ rendered.getByText('test')
2198
+ })
2199
+
2200
+ fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2201
+
2202
+ await waitFor(() => {
2203
+ rendered.getByText('test')
2204
+ })
2205
+
2206
+ expect(states[0]).toMatchObject({ data: undefined })
2207
+ expect(states[1]).toMatchObject({ data: 'test' })
2208
+
2209
+ // make sure no additional renders happen
2210
+ await sleep(50)
2211
+ expect(states.length).toBe(2)
2212
+ })
2213
+ it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
2214
+ const key = queryKey()
2215
+ const states: UseQueryResult<string>[] = []
2216
+
2217
+ function Page() {
2218
+ const state = useQuery({
2219
+ queryKey: key,
2220
+ queryFn: async () => {
2221
+ await sleep(5)
2222
+ return 'test'
2223
+ },
2224
+
2225
+ notifyOnChangeProps: ['data'],
2226
+ })
2227
+
2228
+ states.push(state)
2229
+
2230
+ return (
2231
+ <>
2232
+ <button
2233
+ onClick={async () => {
2234
+ await state.refetch()
2235
+ }}
2236
+ >
2237
+ refetch
2238
+ </button>
2239
+
2240
+ <div>{state.data}</div>
2241
+ </>
2242
+ )
2243
+ }
2244
+
2245
+ const rendered = renderWithClient(queryClient, <Page />)
2246
+
2247
+ await waitFor(() => {
2248
+ rendered.getByText('test')
2249
+ })
2250
+
2251
+ fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2252
+
2253
+ // sleep is required to make sure no additional renders happen after click
2254
+ await sleep(20)
2255
+
2256
+ expect(states.length).toBe(2)
2257
+ expect(states[0]).toMatchObject({
2258
+ data: undefined,
2259
+ status: 'pending',
2260
+ isFetching: true,
2261
+ })
2262
+ expect(states[1]).toMatchObject({
2263
+ data: 'test',
2264
+ status: 'success',
2265
+ isFetching: false,
2266
+ })
2267
+ })
2268
+
2269
+ // See https://github.com/TanStack/query/discussions/5588
2270
+ describe('function', () => {
2271
+ it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
2272
+ const key = queryKey()
2273
+ const states: UseQueryResult<string>[] = []
2274
+
2275
+ function Page() {
2276
+ const state = useQuery({
2277
+ queryKey: key,
2278
+ queryFn: async () => {
2279
+ await sleep(5)
2280
+ return 'test'
2281
+ },
2282
+ notifyOnChangeProps: () => ['data'],
2283
+ })
2284
+
2285
+ states.push(state)
2286
+
2287
+ return (
2288
+ <>
2289
+ <button
2290
+ onClick={async () => {
2291
+ await state.refetch()
2292
+ }}
2293
+ >
2294
+ refetch
2295
+ </button>
2296
+
2297
+ <div>{state.data}</div>
2298
+ </>
2299
+ )
2300
+ }
2301
+
2302
+ const rendered = renderWithClient(queryClient, <Page />)
2303
+
2304
+ await waitFor(() => {
2305
+ rendered.getByText('test')
2306
+ })
2307
+
2308
+ fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2309
+
2310
+ // sleep is required to make sure no additional renders happen after click
2311
+ await sleep(20)
2312
+
2313
+ expect(states.length).toBe(2)
2314
+ expect(states[0]).toMatchObject({
2315
+ data: undefined,
2316
+ status: 'pending',
2317
+ isFetching: true,
2318
+ })
2319
+ expect(states[1]).toMatchObject({
2320
+ data: 'test',
2321
+ status: 'success',
2322
+ isFetching: false,
2323
+ })
2324
+ })
2325
+
2326
+ it('should not re-render when change props are not actively being tracked', async () => {
2327
+ const key = queryKey()
2328
+ const states: UseQueryResult<string>[] = []
2280
2329
 
2281
- expect(states.length).toBe(3)
2282
- expect(states[0]).toMatchObject({ isStale: true })
2283
- expect(states[1]).toMatchObject({ isStale: false })
2284
- expect(states[2]).toMatchObject({ isStale: true })
2285
- })
2330
+ function Page() {
2331
+ const fetchCounterRef = React.useRef(0)
2332
+ const trackChangesRef = React.useRef(true)
2286
2333
 
2287
- it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
2288
- const key = queryKey()
2289
- const states: UseQueryResult<string>[] = []
2334
+ const notifyOnChangeProps = React.useCallback(() => {
2335
+ return trackChangesRef.current ? 'all' : []
2336
+ }, [])
2290
2337
 
2291
- function Page() {
2292
- const state = useQuery({
2293
- queryKey: key,
2294
- queryFn: async () => {
2295
- await sleep(5)
2296
- return 'test'
2297
- },
2338
+ const state = useQuery({
2339
+ queryKey: key,
2340
+ queryFn: async () => {
2341
+ await sleep(5)
2342
+ fetchCounterRef.current++
2343
+ return `fetch counter: ${fetchCounterRef.current}`
2344
+ },
2345
+ notifyOnChangeProps,
2346
+ })
2298
2347
 
2299
- notifyOnChangeProps: ['data'],
2300
- })
2348
+ states.push(state)
2301
2349
 
2302
- states.push(state)
2350
+ return (
2351
+ <>
2352
+ <button
2353
+ onClick={async () => {
2354
+ await state.refetch()
2355
+ }}
2356
+ >
2357
+ refetch
2358
+ </button>
2359
+ <button
2360
+ onClick={() => {
2361
+ trackChangesRef.current = true
2362
+ }}
2363
+ >
2364
+ enableTracking
2365
+ </button>
2366
+ <button
2367
+ onClick={() => {
2368
+ trackChangesRef.current = false
2369
+ }}
2370
+ >
2371
+ disableTracking
2372
+ </button>
2373
+
2374
+ <div>{state.data}</div>
2375
+ </>
2376
+ )
2377
+ }
2303
2378
 
2304
- return (
2305
- <>
2306
- <button
2307
- onClick={async () => {
2308
- await state.refetch()
2309
- }}
2310
- >
2311
- refetch
2312
- </button>
2379
+ const rendered = renderWithClient(queryClient, <Page />)
2380
+ await waitFor(() => {
2381
+ rendered.getByText('fetch counter: 1')
2382
+ })
2313
2383
 
2314
- <div>{state.data}</div>
2315
- </>
2316
- )
2317
- }
2384
+ expect(states.length).toBe(2)
2385
+ expect(states[0]).toMatchObject({
2386
+ data: undefined,
2387
+ isFetching: true,
2388
+ status: 'pending',
2389
+ })
2390
+ expect(states[1]).toMatchObject({
2391
+ data: 'fetch counter: 1',
2392
+ status: 'success',
2393
+ isFetching: false,
2394
+ })
2318
2395
 
2319
- const rendered = renderWithClient(queryClient, <Page />)
2396
+ // disable tracking and refetch to check for re-renders
2397
+ fireEvent.click(
2398
+ rendered.getByRole('button', { name: 'disableTracking' }),
2399
+ )
2400
+ fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2320
2401
 
2321
- await waitFor(() => {
2322
- rendered.getByText('test')
2323
- })
2402
+ // sleep is required to make sure no additional renders happen after click
2403
+ await sleep(20)
2404
+ // still expect to only have two re-renders from the initial fetch
2405
+ expect(states.length).toBe(2)
2324
2406
 
2325
- fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2407
+ // enable tracking and refetch to check for re-renders
2408
+ fireEvent.click(
2409
+ rendered.getByRole('button', { name: 'enableTracking' }),
2410
+ )
2411
+ fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
2326
2412
 
2327
- // sleep is required to make sure no additional renders happen after click
2328
- await sleep(20)
2413
+ // sleep is required to make sure no additional renders happen after click
2414
+ await sleep(20)
2329
2415
 
2330
- expect(states.length).toBe(2)
2331
- expect(states[0]).toMatchObject({
2332
- data: undefined,
2333
- status: 'pending',
2334
- isFetching: true,
2335
- })
2336
- expect(states[1]).toMatchObject({
2337
- data: 'test',
2338
- status: 'success',
2339
- isFetching: false,
2416
+ expect(states.length).toBe(4)
2417
+ expect(states[2]).toMatchObject({
2418
+ data: 'fetch counter: 2',
2419
+ status: 'success',
2420
+ isFetching: true,
2421
+ })
2422
+ expect(states[3]).toMatchObject({
2423
+ data: 'fetch counter: 3',
2424
+ status: 'success',
2425
+ isFetching: false,
2426
+ })
2427
+ })
2340
2428
  })
2341
2429
  })
2342
2430
 
@@ -2434,53 +2522,6 @@ describe('useQuery', () => {
2434
2522
  expect(renders).toBe(2)
2435
2523
  })
2436
2524
 
2437
- it('should batch re-renders including hook callbacks', async () => {
2438
- const key = queryKey()
2439
-
2440
- let renders = 0
2441
- let callbackCount = 0
2442
-
2443
- const queryFn = async () => {
2444
- await sleep(10)
2445
- return 'data'
2446
- }
2447
-
2448
- function Page() {
2449
- const [count, setCount] = React.useState(0)
2450
- useQuery({
2451
- queryKey: key,
2452
- queryFn,
2453
- onSuccess: () => {
2454
- setCount((x) => x + 1)
2455
- },
2456
- })
2457
- useQuery({
2458
- queryKey: key,
2459
- queryFn,
2460
- onSuccess: () => {
2461
- setCount((x) => x + 1)
2462
- },
2463
- })
2464
-
2465
- React.useEffect(() => {
2466
- renders++
2467
- callbackCount = count
2468
- })
2469
-
2470
- return <div>count: {count}</div>
2471
- }
2472
-
2473
- const rendered = renderWithClient(queryClient, <Page />)
2474
-
2475
- await waitFor(() => rendered.getByText('count: 2'))
2476
-
2477
- // Should be 3 instead of 5
2478
- expect(renders).toBe(3)
2479
-
2480
- // Both callbacks should have been executed
2481
- expect(callbackCount).toBe(2)
2482
- })
2483
-
2484
2525
  it('should render latest data even if react has discarded certain renders', async () => {
2485
2526
  const key = queryKey()
2486
2527
 
@@ -2557,33 +2598,6 @@ describe('useQuery', () => {
2557
2598
  rendered.getByText('status: pending')
2558
2599
  })
2559
2600
 
2560
- // See https://github.com/tannerlinsley/react-query/issues/147
2561
- it('should not pass stringified variables to query function', async () => {
2562
- const key = queryKey()
2563
- const variables = { number: 5, boolean: false, object: {}, array: [] }
2564
- type CustomQueryKey = [typeof key, typeof variables]
2565
- const states: UseQueryResult<CustomQueryKey>[] = []
2566
-
2567
- const queryFn = async (ctx: QueryFunctionContext<CustomQueryKey>) => {
2568
- await sleep(10)
2569
- return ctx.queryKey
2570
- }
2571
-
2572
- function Page() {
2573
- const state = useQuery({ queryKey: [key, variables], queryFn })
2574
- states.push(state)
2575
- return <div>{state.status}</div>
2576
- }
2577
-
2578
- const rendered = renderWithClient(queryClient, <Page />)
2579
-
2580
- await waitFor(() => {
2581
- rendered.getByText('success')
2582
- })
2583
-
2584
- expect(states[1]?.data).toEqual([key, variables])
2585
- })
2586
-
2587
2601
  it('should not refetch query on focus when `enabled` is set to `false`', async () => {
2588
2602
  const key = queryKey()
2589
2603
  const queryFn = vi.fn<unknown[], string>().mockReturnValue('data')
@@ -2723,24 +2737,24 @@ describe('useQuery', () => {
2723
2737
  refetchOnWindowFocus: 'always',
2724
2738
  })
2725
2739
  states.push(state)
2726
- return null
2740
+ return (
2741
+ <div>
2742
+ <div>
2743
+ data: {state.data}, isFetching: {String(state.isFetching)}
2744
+ </div>
2745
+ </div>
2746
+ )
2727
2747
  }
2728
2748
 
2729
- renderWithClient(queryClient, <Page />)
2749
+ const rendered = renderWithClient(queryClient, <Page />)
2730
2750
 
2731
- await sleep(20)
2751
+ await waitFor(() => rendered.getByText('data: 0, isFetching: false'))
2732
2752
 
2733
2753
  act(() => {
2734
2754
  window.dispatchEvent(new Event('visibilitychange'))
2735
2755
  })
2736
2756
 
2737
- await sleep(20)
2738
-
2739
- await waitFor(() => expect(states.length).toBe(4))
2740
- expect(states[0]).toMatchObject({ data: undefined, isFetching: true })
2741
- expect(states[1]).toMatchObject({ data: 0, isFetching: false })
2742
- expect(states[2]).toMatchObject({ data: 0, isFetching: true })
2743
- expect(states[3]).toMatchObject({ data: 1, isFetching: false })
2757
+ await waitFor(() => rendered.getByText('data: 1, isFetching: false'))
2744
2758
  })
2745
2759
 
2746
2760
  it('should calculate focus behaviour for `refetchOnWindowFocus` depending on function', async () => {
@@ -2896,7 +2910,7 @@ describe('useQuery', () => {
2896
2910
  await waitFor(() => rendered.getByText('Error test jaylen'))
2897
2911
  })
2898
2912
 
2899
- it('should throw error if queryFn throws and throwErrors is in use', async () => {
2913
+ it('should throw error if queryFn throws and throwOnError is in use', async () => {
2900
2914
  const consoleMock = vi
2901
2915
  .spyOn(console, 'error')
2902
2916
  .mockImplementation(() => undefined)
@@ -2907,7 +2921,7 @@ describe('useQuery', () => {
2907
2921
  queryKey: key,
2908
2922
  queryFn: () => Promise.reject(new Error('Error test jaylen')),
2909
2923
  retry: false,
2910
- throwErrors: true,
2924
+ throwOnError: true,
2911
2925
  })
2912
2926
 
2913
2927
  return (
@@ -2929,7 +2943,7 @@ describe('useQuery', () => {
2929
2943
  consoleMock.mockRestore()
2930
2944
  })
2931
2945
 
2932
- it('should update with data if we observe no properties and throwErrors', async () => {
2946
+ it('should update with data if we observe no properties and throwOnError', async () => {
2933
2947
  const key = queryKey()
2934
2948
 
2935
2949
  let result: UseQueryResult<string> | undefined
@@ -2938,7 +2952,7 @@ describe('useQuery', () => {
2938
2952
  const query = useQuery({
2939
2953
  queryKey: key,
2940
2954
  queryFn: () => Promise.resolve('data'),
2941
- throwErrors: true,
2955
+ throwOnError: true,
2942
2956
  })
2943
2957
 
2944
2958
  React.useEffect(() => {
@@ -2966,7 +2980,7 @@ describe('useQuery', () => {
2966
2980
  queryFn: () => Promise.reject(new Error('Local Error')),
2967
2981
 
2968
2982
  retry: false,
2969
- throwErrors: (err) => err.message !== 'Local Error',
2983
+ throwOnError: (err) => err.message !== 'Local Error',
2970
2984
  })
2971
2985
 
2972
2986
  return (
@@ -3001,7 +3015,7 @@ describe('useQuery', () => {
3001
3015
  queryFn: () => Promise.reject(new Error('Remote Error')),
3002
3016
 
3003
3017
  retry: false,
3004
- throwErrors: (err) => err.message !== 'Local Error',
3018
+ throwOnError: (err) => err.message !== 'Local Error',
3005
3019
  })
3006
3020
 
3007
3021
  return (
@@ -3758,6 +3772,7 @@ describe('useQuery', () => {
3758
3772
  })
3759
3773
  act(() => setPrefetched(true))
3760
3774
  }
3775
+
3761
3776
  prefetch()
3762
3777
  }, [])
3763
3778
 
@@ -5200,7 +5215,7 @@ describe('useQuery', () => {
5200
5215
 
5201
5216
  describe('networkMode online', () => {
5202
5217
  it('online queries should not start fetching if you are offline', async () => {
5203
- const onlineMock = mockNavigatorOnLine(false)
5218
+ const onlineMock = mockOnlineManagerIsOnline(false)
5204
5219
 
5205
5220
  const key = queryKey()
5206
5221
  const states: Array<any> = []
@@ -5229,10 +5244,11 @@ describe('useQuery', () => {
5229
5244
  }
5230
5245
 
5231
5246
  const rendered = renderWithClient(queryClient, <Page />)
5247
+ window.dispatchEvent(new Event('offline'))
5232
5248
 
5233
5249
  await waitFor(() => rendered.getByText('status: pending, isPaused: true'))
5234
5250
 
5235
- onlineMock.mockReturnValue(true)
5251
+ onlineMock.mockRestore()
5236
5252
  window.dispatchEvent(new Event('online'))
5237
5253
 
5238
5254
  await waitFor(() =>
@@ -5243,8 +5259,6 @@ describe('useQuery', () => {
5243
5259
  })
5244
5260
 
5245
5261
  expect(states).toEqual(['paused', 'fetching', 'idle'])
5246
-
5247
- onlineMock.mockRestore()
5248
5262
  })
5249
5263
 
5250
5264
  it('online queries should not refetch if you are offline', async () => {
@@ -5282,7 +5296,9 @@ describe('useQuery', () => {
5282
5296
 
5283
5297
  await waitFor(() => rendered.getByText('data: data1'))
5284
5298
 
5285
- const onlineMock = mockNavigatorOnLine(false)
5299
+ const onlineMock = mockOnlineManagerIsOnline(false)
5300
+ window.dispatchEvent(new Event('offline'))
5301
+
5286
5302
  fireEvent.click(rendered.getByRole('button', { name: /invalidate/i }))
5287
5303
 
5288
5304
  await waitFor(() =>
@@ -5292,7 +5308,7 @@ describe('useQuery', () => {
5292
5308
  )
5293
5309
  await waitFor(() => rendered.getByText('failureReason: null'))
5294
5310
 
5295
- onlineMock.mockReturnValue(true)
5311
+ onlineMock.mockRestore()
5296
5312
  window.dispatchEvent(new Event('online'))
5297
5313
 
5298
5314
  await waitFor(() =>
@@ -5311,8 +5327,6 @@ describe('useQuery', () => {
5311
5327
  await waitFor(() => {
5312
5328
  expect(rendered.getByText('data: data2')).toBeInTheDocument()
5313
5329
  })
5314
-
5315
- onlineMock.mockRestore()
5316
5330
  })
5317
5331
 
5318
5332
  it('online queries should not refetch if you are offline and refocus', async () => {
@@ -5348,7 +5362,7 @@ describe('useQuery', () => {
5348
5362
 
5349
5363
  await waitFor(() => rendered.getByText('data: data1'))
5350
5364
 
5351
- const onlineMock = mockNavigatorOnLine(false)
5365
+ const onlineMock = mockOnlineManagerIsOnline(false)
5352
5366
  fireEvent.click(rendered.getByRole('button', { name: /invalidate/i }))
5353
5367
 
5354
5368
  await waitFor(() =>
@@ -5394,7 +5408,7 @@ describe('useQuery', () => {
5394
5408
  )
5395
5409
  }
5396
5410
 
5397
- const onlineMock = mockNavigatorOnLine(false)
5411
+ const onlineMock = mockOnlineManagerIsOnline(false)
5398
5412
 
5399
5413
  const rendered = renderWithClient(queryClient, <Page />)
5400
5414
 
@@ -5445,7 +5459,7 @@ describe('useQuery', () => {
5445
5459
  )
5446
5460
  }
5447
5461
 
5448
- const onlineMock = mockNavigatorOnLine(false)
5462
+ const onlineMock = mockOnlineManagerIsOnline(false)
5449
5463
 
5450
5464
  const rendered = renderWithClient(queryClient, <Page />)
5451
5465
 
@@ -5499,9 +5513,10 @@ describe('useQuery', () => {
5499
5513
  )
5500
5514
  }
5501
5515
 
5502
- const onlineMock = mockNavigatorOnLine(false)
5516
+ const onlineMock = mockOnlineManagerIsOnline(false)
5503
5517
 
5504
5518
  const rendered = renderWithClient(queryClient, <Page />)
5519
+ window.dispatchEvent(new Event('offline'))
5505
5520
 
5506
5521
  await waitFor(() =>
5507
5522
  rendered.getByText('status: success, fetchStatus: paused'),
@@ -5524,7 +5539,7 @@ describe('useQuery', () => {
5524
5539
  window.dispatchEvent(new Event('visibilitychange'))
5525
5540
  })
5526
5541
 
5527
- onlineMock.mockReturnValue(true)
5542
+ onlineMock.mockRestore()
5528
5543
  act(() => {
5529
5544
  window.dispatchEvent(new Event('online'))
5530
5545
  })
@@ -5537,7 +5552,6 @@ describe('useQuery', () => {
5537
5552
  })
5538
5553
 
5539
5554
  expect(count).toBe(1)
5540
- onlineMock.mockRestore()
5541
5555
  })
5542
5556
 
5543
5557
  it('online queries should pause retries if you are offline', async () => {
@@ -5570,13 +5584,11 @@ describe('useQuery', () => {
5570
5584
  const rendered = renderWithClient(queryClient, <Page />)
5571
5585
 
5572
5586
  await waitFor(() =>
5573
- rendered.getByText(
5574
- 'status: pending, fetchStatus: fetching, failureCount: 1',
5575
- ),
5587
+ rendered.getByText(/status: pending, fetchStatus: fetching/i),
5576
5588
  )
5577
- await waitFor(() => rendered.getByText('failureReason: failed1'))
5578
5589
 
5579
- const onlineMock = mockNavigatorOnLine(false)
5590
+ const onlineMock = mockOnlineManagerIsOnline(false)
5591
+ window.dispatchEvent(new Event('offline'))
5580
5592
 
5581
5593
  await sleep(20)
5582
5594
 
@@ -5637,17 +5649,19 @@ describe('useQuery', () => {
5637
5649
  )
5638
5650
  }
5639
5651
 
5640
- const onlineMock = mockNavigatorOnLine(false)
5652
+ const onlineMock = mockOnlineManagerIsOnline(false)
5641
5653
 
5642
5654
  const rendered = renderWithClient(queryClient, <Page />)
5643
5655
 
5656
+ window.dispatchEvent(new Event('offline'))
5657
+
5644
5658
  await waitFor(() =>
5645
5659
  rendered.getByText('status: pending, fetchStatus: paused'),
5646
5660
  )
5647
5661
 
5648
5662
  fireEvent.click(rendered.getByRole('button', { name: /hide/i }))
5649
5663
 
5650
- onlineMock.mockReturnValue(true)
5664
+ onlineMock.mockRestore()
5651
5665
  window.dispatchEvent(new Event('online'))
5652
5666
 
5653
5667
  await waitFor(() => {
@@ -5660,8 +5674,6 @@ describe('useQuery', () => {
5660
5674
  // give it a bit more time to make sure queryFn is not called again
5661
5675
  await sleep(15)
5662
5676
  expect(count).toBe(1)
5663
-
5664
- onlineMock.mockRestore()
5665
5677
  })
5666
5678
 
5667
5679
  it('online queries should not fetch if paused and we go online when cancelled and no refetchOnReconnect', async () => {
@@ -5694,7 +5706,7 @@ describe('useQuery', () => {
5694
5706
  )
5695
5707
  }
5696
5708
 
5697
- const onlineMock = mockNavigatorOnLine(false)
5709
+ const onlineMock = mockOnlineManagerIsOnline(false)
5698
5710
 
5699
5711
  const rendered = renderWithClient(queryClient, <Page />)
5700
5712
 
@@ -5770,7 +5782,7 @@ describe('useQuery', () => {
5770
5782
  rendered.getByText('status: success, fetchStatus: idle'),
5771
5783
  )
5772
5784
 
5773
- const onlineMock = mockNavigatorOnLine(false)
5785
+ const onlineMock = mockOnlineManagerIsOnline(false)
5774
5786
 
5775
5787
  fireEvent.click(rendered.getByRole('button', { name: /invalidate/i }))
5776
5788
 
@@ -5800,7 +5812,7 @@ describe('useQuery', () => {
5800
5812
 
5801
5813
  describe('networkMode always', () => {
5802
5814
  it('always queries should start fetching even if you are offline', async () => {
5803
- const onlineMock = mockNavigatorOnLine(false)
5815
+ const onlineMock = mockOnlineManagerIsOnline(false)
5804
5816
 
5805
5817
  const key = queryKey()
5806
5818
  let count = 0
@@ -5840,7 +5852,7 @@ describe('useQuery', () => {
5840
5852
  })
5841
5853
 
5842
5854
  it('always queries should not pause retries', async () => {
5843
- const onlineMock = mockNavigatorOnLine(false)
5855
+ const onlineMock = mockOnlineManagerIsOnline(false)
5844
5856
 
5845
5857
  const key = queryKey()
5846
5858
  let count = 0
@@ -5886,7 +5898,7 @@ describe('useQuery', () => {
5886
5898
 
5887
5899
  describe('networkMode offlineFirst', () => {
5888
5900
  it('offlineFirst queries should start fetching if you are offline, but pause retries', async () => {
5889
- const onlineMock = mockNavigatorOnLine(false)
5901
+ const onlineMock = mockOnlineManagerIsOnline(false)
5890
5902
 
5891
5903
  const key = queryKey()
5892
5904
  let count = 0
@@ -5917,6 +5929,8 @@ describe('useQuery', () => {
5917
5929
 
5918
5930
  const rendered = renderWithClient(queryClient, <Page />)
5919
5931
 
5932
+ window.dispatchEvent(new Event('offline'))
5933
+
5920
5934
  await waitFor(() =>
5921
5935
  rendered.getByText(
5922
5936
  'status: pending, fetchStatus: paused, failureCount: 1',
@@ -5926,7 +5940,7 @@ describe('useQuery', () => {
5926
5940
 
5927
5941
  expect(count).toBe(1)
5928
5942
 
5929
- onlineMock.mockReturnValue(true)
5943
+ onlineMock.mockRestore()
5930
5944
  window.dispatchEvent(new Event('online'))
5931
5945
 
5932
5946
  await waitFor(() =>
@@ -5935,8 +5949,6 @@ describe('useQuery', () => {
5935
5949
  await waitFor(() => rendered.getByText('failureReason: failed3'))
5936
5950
 
5937
5951
  expect(count).toBe(3)
5938
-
5939
- onlineMock.mockRestore()
5940
5952
  })
5941
5953
  })
5942
5954
 
@@ -5973,36 +5985,6 @@ describe('useQuery', () => {
5973
5985
  })
5974
5986
  })
5975
5987
 
5976
- it('setQueryData - should not call onSuccess callback of active observers', async () => {
5977
- const key = queryKey()
5978
- const onSuccess = vi.fn()
5979
-
5980
- function Page() {
5981
- const state = useQuery({
5982
- queryKey: key,
5983
- queryFn: () => 'data',
5984
- onSuccess,
5985
- })
5986
- return (
5987
- <div>
5988
- <div>data: {state.data}</div>
5989
- <button onClick={() => queryClient.setQueryData(key, 'newData')}>
5990
- setQueryData
5991
- </button>
5992
- </div>
5993
- )
5994
- }
5995
-
5996
- const rendered = renderWithClient(queryClient, <Page />)
5997
-
5998
- await waitFor(() => rendered.getByText('data: data'))
5999
- fireEvent.click(rendered.getByRole('button', { name: /setQueryData/i }))
6000
- await waitFor(() => rendered.getByText('data: newData'))
6001
-
6002
- expect(onSuccess).toHaveBeenCalledTimes(1)
6003
- expect(onSuccess).toHaveBeenCalledWith('data')
6004
- })
6005
-
6006
5988
  it('setQueryData - should respect updatedAt', async () => {
6007
5989
  const key = queryKey()
6008
5990
 
@@ -6054,6 +6036,7 @@ describe('useQuery', () => {
6054
6036
  </div>
6055
6037
  )
6056
6038
  }
6039
+
6057
6040
  const rendered = renderWithClient(queryClient, <Page />)
6058
6041
  const fetchBtn = rendered.getByRole('button', { name: 'refetch' })
6059
6042
  await waitFor(() => rendered.getByText('data: 1'))
@@ -6085,4 +6068,161 @@ describe('useQuery', () => {
6085
6068
 
6086
6069
  await waitFor(() => rendered.getByText('data: custom client'))
6087
6070
  })
6071
+
6072
+ it('should be notified of updates between create and subscribe', async () => {
6073
+ const key = queryKey()
6074
+
6075
+ function Page() {
6076
+ const mounted = React.useRef<boolean>(false)
6077
+ const { data, status } = useQuery({
6078
+ enabled: false,
6079
+ queryKey: key,
6080
+ queryFn: async () => {
6081
+ await sleep(10)
6082
+ return 5
6083
+ },
6084
+ })
6085
+
6086
+ // this simulates a synchronous update between the time the query is created
6087
+ // and the time it is subscribed to that could be missed otherwise
6088
+ if (!mounted.current) {
6089
+ mounted.current = true
6090
+ queryClient.setQueryData(key, 1)
6091
+ }
6092
+
6093
+ return (
6094
+ <div>
6095
+ <span>status: {status}</span>
6096
+ <span>data: {data}</span>
6097
+ </div>
6098
+ )
6099
+ }
6100
+
6101
+ const rendered = renderWithClient(queryClient, <Page />)
6102
+ await waitFor(() => rendered.getByText('status: success'))
6103
+ await waitFor(() => rendered.getByText('data: 1'))
6104
+ })
6105
+ it('should reuse same data object reference when queryKey changes back to some cached data', async () => {
6106
+ const key = queryKey()
6107
+ const spy = vi.fn()
6108
+
6109
+ async function fetchNumber(id: number) {
6110
+ await sleep(5)
6111
+ return { numbers: { current: { id } } }
6112
+ }
6113
+ function Test() {
6114
+ const [id, setId] = React.useState(1)
6115
+
6116
+ const { data } = useQuery({
6117
+ select: selector,
6118
+ queryKey: [key, 'user', id],
6119
+ queryFn: () => fetchNumber(id),
6120
+ })
6121
+
6122
+ React.useEffect(() => {
6123
+ spy(data)
6124
+ }, [data])
6125
+
6126
+ return (
6127
+ <div>
6128
+ <button name="1" onClick={() => setId(1)}>
6129
+ 1
6130
+ </button>
6131
+ <button name="2" onClick={() => setId(2)}>
6132
+ 2
6133
+ </button>
6134
+ <span>Rendered Id: {data?.id}</span>
6135
+ </div>
6136
+ )
6137
+ }
6138
+
6139
+ function selector(data: any) {
6140
+ return data.numbers.current
6141
+ }
6142
+
6143
+ const rendered = renderWithClient(queryClient, <Test />)
6144
+ expect(spy).toHaveBeenCalledTimes(1)
6145
+
6146
+ spy.mockClear()
6147
+ await waitFor(() => rendered.getByText('Rendered Id: 1'))
6148
+ expect(spy).toHaveBeenCalledTimes(1)
6149
+
6150
+ spy.mockClear()
6151
+ fireEvent.click(rendered.getByRole('button', { name: /2/ }))
6152
+ await waitFor(() => rendered.getByText('Rendered Id: 2'))
6153
+ expect(spy).toHaveBeenCalledTimes(2) // called with undefined because id changed
6154
+
6155
+ spy.mockClear()
6156
+ fireEvent.click(rendered.getByRole('button', { name: /1/ }))
6157
+ await waitFor(() => rendered.getByText('Rendered Id: 1'))
6158
+ expect(spy).toHaveBeenCalledTimes(1)
6159
+
6160
+ spy.mockClear()
6161
+ fireEvent.click(rendered.getByRole('button', { name: /2/ }))
6162
+ await waitFor(() => rendered.getByText('Rendered Id: 2'))
6163
+ expect(spy).toHaveBeenCalledTimes(1)
6164
+ })
6165
+ it('should reuse same data object reference when queryKey changes and placeholderData is present', async () => {
6166
+ const key = queryKey()
6167
+ const spy = vi.fn()
6168
+
6169
+ async function fetchNumber(id: number) {
6170
+ await sleep(5)
6171
+ return { numbers: { current: { id } } }
6172
+ }
6173
+ function Test() {
6174
+ const [id, setId] = React.useState(1)
6175
+
6176
+ const { data } = useQuery({
6177
+ select: selector,
6178
+ queryKey: [key, 'user', id],
6179
+ queryFn: () => fetchNumber(id),
6180
+ placeholderData: { numbers: { current: { id: 99 } } },
6181
+ })
6182
+
6183
+ React.useEffect(() => {
6184
+ spy(data)
6185
+ }, [data])
6186
+
6187
+ return (
6188
+ <div>
6189
+ <button name="1" onClick={() => setId(1)}>
6190
+ 1
6191
+ </button>
6192
+ <button name="2" onClick={() => setId(2)}>
6193
+ 2
6194
+ </button>
6195
+ <span>Rendered Id: {data?.id}</span>
6196
+ </div>
6197
+ )
6198
+ }
6199
+
6200
+ function selector(data: any) {
6201
+ return data.numbers.current
6202
+ }
6203
+
6204
+ const rendered = renderWithClient(queryClient, <Test />)
6205
+ expect(spy).toHaveBeenCalledTimes(1)
6206
+
6207
+ spy.mockClear()
6208
+ await waitFor(() => rendered.getByText('Rendered Id: 99'))
6209
+ await waitFor(() => rendered.getByText('Rendered Id: 1'))
6210
+ expect(spy).toHaveBeenCalledTimes(1)
6211
+
6212
+ spy.mockClear()
6213
+ fireEvent.click(rendered.getByRole('button', { name: /2/ }))
6214
+ await waitFor(() => rendered.getByText('Rendered Id: 99'))
6215
+ await waitFor(() => rendered.getByText('Rendered Id: 2'))
6216
+ expect(spy).toHaveBeenCalledTimes(2) // called with undefined because id changed
6217
+
6218
+ spy.mockClear()
6219
+ fireEvent.click(rendered.getByRole('button', { name: /1/ }))
6220
+ await waitFor(() => rendered.getByText('Rendered Id: 1'))
6221
+ expect(spy).toHaveBeenCalledTimes(1)
6222
+
6223
+ spy.mockClear()
6224
+ fireEvent.click(rendered.getByRole('button', { name: /2/ }))
6225
+ await waitFor(() => rendered.getByText('Rendered Id: 2'))
6226
+ expect(spy).toHaveBeenCalledTimes(1)
6227
+ })
6088
6228
  })