EscolaLMS/sdk

View on GitHub
src/react/context/states.ts

Summary

Maintainability
D
2 days
Test Coverage
import * as API from "./../../types/api";
import {
  ContextListState,
  ContextPaginatedMetaState,
  ContextStateValue,
} from "./types";

type fetchDataType<T> =
  | {
      controller?: string;
      controllers?: Record<string, AbortController | null>;
      mode: "paginated";
      fetchAction: Promise<API.DefaultMetaResponse<T>>;
      setState: React.Dispatch<
        React.SetStateAction<ContextPaginatedMetaState<T>>
      >;
      onError?: (error: API.DefaultResponseError | any) => void;
    }
  | {
      id?: number | string;
      controller?: string;
      controllers?: Record<string, AbortController | null>;
      mode: "value";
      fetchAction: Promise<API.DefaultResponse<T>>;
      setState: React.Dispatch<React.SetStateAction<ContextStateValue<T>>>;
      onError?: (error: API.DefaultResponseError | any) => void;
    }
  | {
      controller?: string;
      controllers?: Record<string, AbortController | null>;
      mode: "list";
      fetchAction: Promise<API.DefaultResponse<T[]>>;
      setState: React.Dispatch<React.SetStateAction<ContextListState<T>>>;
      onError?: (error: API.DefaultResponseError | any) => void;
    };

export function fetchDataType<T>(
  params: fetchDataType<T> & { mode: "paginated" }
): Promise<API.DefaultMetaResponse<T>>;
export function fetchDataType<T>(
  params: fetchDataType<T> & { mode: "value" | "list" }
): Promise<API.DefaultResponse<T>>;

export async function fetchDataType<T>(params: fetchDataType<T>) {
  const { setState, fetchAction, mode, controller, controllers, onError } =
    params;

  if (mode === "paginated") {
    setState((prevState) => ({ ...prevState, loading: true }));

    try {
      const response = await fetchAction;

      if (mode === "paginated") {
        if (response.success) {
          setState((prevState) => ({
            ...prevState,
            loading: false,
            list: response as API.PaginatedMetaList<T>,
            error: undefined,
          }));
        }
      }

      return response;
    } catch (error: any) {
      if (error.name === "AbortError") {
        if (controllers && controller && controllers[controller]) {
          controllers[controller] = null;
        }
        return error;
      }

      if (onError) {
        onError(error);
      }

      setState((prevState: any) => ({
        ...prevState,
        loading: false,
        error: error,
      }));

      return error;
    }
  }

  if (mode === "value") {
    const { id } = params;

    id
      ? setState((prevState) => ({
          ...prevState,
          loading: true,
          byId: prevState.byId
            ? {
                ...prevState.byId,
                [id]: prevState.byId[id]
                  ? { ...prevState.byId[id], loading: true }
                  : { loading: true },
              }
            : {
                [id]: {
                  loading: true,
                  error: undefined,
                },
              },
        }))
      : setState((prevState) => ({ ...prevState, loading: true }));

    try {
      const response = await fetchAction;

      if (response.success) {
        if (id) {
          setState((prevState) => {
            return {
              loading: false,
              value: response.data,
              error: undefined,
              byId: prevState.byId
                ? {
                    ...prevState.byId,
                    [id]: {
                      value: response.data,
                      loading: false,
                      error: undefined,
                    },
                  }
                : {
                    [id]: {
                      value: response.data,
                      loading: false,
                      error: undefined,
                    },
                  },
            };
          });

          return response;
        } else {
          setState({
            loading: false,
            value: response.data,
            error: undefined,
          });

          return response;
        }
      }
    } catch (error) {
      if (params.id) {
        const { id } = params;

        setState((prevState: any) => ({
          ...prevState,
          loading: false,
          error: error,
          byId: prevState.byId
            ? {
                ...prevState.byId,
                [id]: {
                  ...prevState.byId[id],
                  loading: false,
                  error: error,
                },
              }
            : {
                [id]: {
                  loading: false,
                  error: error,
                },
              },
        }));
      } else {
        setState((prevState: any) => ({
          ...prevState,
          loading: false,
          error: error,
        }));
      }

      return error;
    }
  }

  if (mode === "list") {
    setState((prevState) => ({ ...prevState, loading: true }));
    try {
      const response = await fetchAction;

      if (response.success) {
        setState({
          loading: false,
          list: response.data,
          error: undefined,
        });

        return response;
      }
    } catch (error: any) {
      if (error.name === "AbortError") {
        if (controllers && controller && controllers[controller]) {
          controllers[controller] = null;
        }
        return;
      }

      if (onError) {
        onError(error);
      }

      setState((prevState: any) => ({
        ...prevState,
        loading: false,
        error: error,
      }));

      return error;
    }
  }

  return undefined;
}