queryClient.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import * as reactQuery from '@tanstack/react-query';
  2. import {QueryClientConfig} from '@tanstack/react-query';
  3. import RequestError from 'sentry/utils/requestError/requestError';
  4. import useApi from 'sentry/utils/useApi';
  5. type QueryKeyEndpointOptions = {
  6. query?: Record<string, any>;
  7. };
  8. export type QueryKey =
  9. | readonly [url: string]
  10. | readonly [url: string, options: QueryKeyEndpointOptions];
  11. interface UseQueryOptions<TQueryFnData, TError = RequestError, TData = TQueryFnData>
  12. extends Omit<
  13. reactQuery.UseQueryOptions<TQueryFnData, TError, TData, QueryKey>,
  14. 'queryKey' | 'queryFn'
  15. > {
  16. /**
  17. * staleTime is the amount of time (in ms) before cached data gets marked as stale.
  18. * Once data is marked stale, it will be refreshed on the next refetch event, which by default is when:
  19. * - The hook is mounted (configure with `refetchOnMount` option)
  20. * - The window is refocused (configure with `refetchOnWindowFocus` option)
  21. *
  22. * Use `staleTime: 0` if you need your data to always be up to date and don't mind excess refetches.
  23. * Be careful with this, especially if your hook is used at the root level or in multiple components.
  24. *
  25. * Use `staleTime: Infinity` if the data should never change, or changes very irregularly.
  26. * Note that the cached entries are garbage collected after 5 minutes of being unused (configure with `cacheTime`).
  27. *
  28. * Otherwise, provide a reasonable number (in ms) for your use case. Remember that the cache
  29. * can be updated or invalidated manually with QueryClient if you neeed to do so.
  30. */
  31. staleTime: number;
  32. }
  33. // We are not overriding any defaults options for stale time, retries, etc.
  34. // See https://tanstack.com/query/v4/docs/guides/important-defaults
  35. const DEFAULT_QUERY_CLIENT_CONFIG: QueryClientConfig = {};
  36. function isQueryFn<TQueryFnData, TError, TData>(
  37. queryFnOrQueryOptions?:
  38. | reactQuery.QueryFunction<TQueryFnData, QueryKey>
  39. | UseQueryOptions<TQueryFnData, TError, TData>
  40. ): queryFnOrQueryOptions is reactQuery.QueryFunction<TQueryFnData, QueryKey> {
  41. return typeof queryFnOrQueryOptions === 'function';
  42. }
  43. /**
  44. * Wraps React Query's useQuery for consistent usage in the Sentry app.
  45. * Query keys should be an array which include an endpoint URL and options such as query params.
  46. * This wrapper will execute the request using the query key URL, but if you need custom behavior
  47. * you may supply your own query function as the second argument.
  48. *
  49. * See https://tanstack.com/query/v4/docs/overview for docs on React Query.
  50. *
  51. * Example usage:
  52. *
  53. * const {data, isLoading, isError} = useQuery<EventsResponse>(
  54. * ['/events', {query: {limit: 50}}],
  55. * {staleTime: 0}
  56. * );
  57. */
  58. function useQuery<TQueryFnData, TError = RequestError, TData = TQueryFnData>(
  59. queryKey: QueryKey,
  60. queryOptions: UseQueryOptions<TQueryFnData, TError, TData>
  61. ): reactQuery.UseQueryResult<TData, TError>;
  62. /**
  63. * Example usage with custom query function:
  64. *
  65. * const { data, isLoading, isError } = useQuery<EventsResponse>(
  66. * ['events', {limit: 50}],
  67. * () => api.requestPromise({limit: 50}),
  68. * {staleTime: 0}
  69. * )
  70. */
  71. function useQuery<TQueryFnData, TError = RequestError, TData = TQueryFnData>(
  72. queryKey: QueryKey,
  73. queryFn: reactQuery.QueryFunction<TQueryFnData, QueryKey>,
  74. queryOptions?: UseQueryOptions<TQueryFnData, TError, TData>
  75. ): reactQuery.UseQueryResult<TData, TError>;
  76. function useQuery<TQueryFnData, TError = RequestError, TData = TQueryFnData>(
  77. queryKey: QueryKey,
  78. queryFnOrQueryOptions:
  79. | reactQuery.QueryFunction<TQueryFnData, QueryKey>
  80. | UseQueryOptions<TQueryFnData, TError, TData>,
  81. queryOptions?: UseQueryOptions<TQueryFnData, TError, TData>
  82. ): reactQuery.UseQueryResult<TData, TError> {
  83. // XXX: We need to set persistInFlight to disable query cancellation on unmount.
  84. // The current implementation of our API client does not reject on query
  85. // cancellation, which causes React Query to never update from the isLoading state.
  86. // This matches the library default as well: https://tanstack.com/query/v4/docs/guides/query-cancellation#default-behavior
  87. const api = useApi({persistInFlight: true});
  88. const [path, endpointOptions] = queryKey;
  89. const defaultQueryFn: reactQuery.QueryFunction<TQueryFnData, QueryKey> = () =>
  90. api.requestPromise(path, {
  91. method: 'GET',
  92. query: endpointOptions?.query,
  93. });
  94. const queryFn = isQueryFn(queryFnOrQueryOptions)
  95. ? queryFnOrQueryOptions
  96. : defaultQueryFn;
  97. const options =
  98. queryOptions ??
  99. (isQueryFn(queryFnOrQueryOptions) ? undefined : queryFnOrQueryOptions);
  100. return reactQuery.useQuery(queryKey, queryFn, options);
  101. }
  102. // eslint-disable-next-line import/export
  103. export * from '@tanstack/react-query';
  104. // eslint-disable-next-line import/export
  105. export {DEFAULT_QUERY_CLIENT_CONFIG, useQuery, UseQueryOptions};