use-data.ts 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { useLogger } from "@pythnetwork/app-logger";
  2. import { useCallback } from "react";
  3. import useSWR, { type KeyedMutator } from "swr";
  4. export const useData = <T>(
  5. key: Parameters<typeof useSWR<T>>[0],
  6. fetcher?: Parameters<typeof useSWR<T>>[1],
  7. config?: Parameters<typeof useSWR<T>>[2],
  8. ) => {
  9. const { data, isLoading, mutate, ...rest } = useSWR(
  10. key,
  11. // eslint-disable-next-line unicorn/no-null
  12. fetcher ?? null,
  13. config,
  14. );
  15. const error = rest.error as unknown;
  16. const logger = useLogger();
  17. const reset = useCallback(() => {
  18. mutate(undefined).catch(() => {
  19. /* no-op */
  20. });
  21. }, [mutate]);
  22. if (error) {
  23. logger.error(error);
  24. return State.ErrorState(new UseDataError(error), reset);
  25. } else if (isLoading) {
  26. return State.Loading();
  27. } else if (data) {
  28. return State.Loaded(data, mutate);
  29. } else {
  30. return State.NotLoaded();
  31. }
  32. };
  33. export enum StateType {
  34. NotLoaded,
  35. Loading,
  36. Loaded,
  37. Error,
  38. }
  39. const State = {
  40. NotLoaded: () => ({ type: StateType.NotLoaded as const }),
  41. Loading: () => ({ type: StateType.Loading as const }),
  42. Loaded: <T>(data: T, mutate: KeyedMutator<T>) => ({
  43. type: StateType.Loaded as const,
  44. mutate,
  45. data,
  46. }),
  47. ErrorState: (error: UseDataError, reset: () => void) => ({
  48. type: StateType.Error as const,
  49. error,
  50. reset,
  51. }),
  52. };
  53. class UseDataError extends Error {
  54. constructor(cause: unknown) {
  55. super(cause instanceof Error ? cause.message : "");
  56. this.name = "UseDataError";
  57. this.cause = cause;
  58. }
  59. }