You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
4.5KB

  1. import { useCallback, useMemo, useState } from "react";
  2. import { APICommonResponse, apiRequest, ResultCode } from "api";
  3. import { UseFormReturn } from "react-hook-form";
  4. import { Dictionary } from "@types";
  5. import { useSnackbar } from "notistack";
  6. export const APIErrorType = {
  7. NONE: "none",
  8. INPUT: "input",
  9. SERVER: "server",
  10. EXCLUSIVE: "exclusive",
  11. } as const;
  12. export type APIErrorType = (typeof APIErrorType)[keyof typeof APIErrorType];
  13. export default function useAPICall<
  14. T extends object,
  15. U extends APICommonResponse
  16. >({
  17. apiMethod,
  18. onSuccess,
  19. onFailed,
  20. form,
  21. successMessage = false,
  22. failedMessage = false,
  23. }: {
  24. apiMethod: (sendData: T) => Promise<U | null>;
  25. onSuccess?: (res: U, sendData: T) => void;
  26. onFailed?: (res: APICommonResponse | null) => void;
  27. form?: UseFormReturn<any>;
  28. successMessage?: ((res: U) => string) | boolean;
  29. failedMessage?: ((res: APICommonResponse | null) => string) | boolean;
  30. }) {
  31. const [sending, setSending] = useState(false);
  32. const [sendData, setSendData] = useState<T | null>(null);
  33. const [result, setResult] = useState<ResultCode | null>(null);
  34. const [errors, setErrors] = useState<Dictionary>({});
  35. const [validationError, setValidationError] = useState(false);
  36. const [exclusiveError, setExclusiveError] = useState(false);
  37. const [generalErrorMessage, setGeneralErrorMessage] = useState("");
  38. const { enqueueSnackbar } = useSnackbar();
  39. const clearErrors = () => {
  40. setResult(null);
  41. setErrors({});
  42. setSendData(null);
  43. setValidationError(false);
  44. setExclusiveError(false);
  45. setGeneralErrorMessage("");
  46. };
  47. // 入力項目に対してエラーレスポンスがあるか、ないかでエラーのタイプを設定する
  48. const errorMode = useMemo<APIErrorType>(() => {
  49. if (generalErrorMessage !== "") return APIErrorType.INPUT;
  50. if (exclusiveError) return APIErrorType.EXCLUSIVE;
  51. if (validationError) return APIErrorType.INPUT;
  52. if (result === null) return APIErrorType.NONE;
  53. if (result === ResultCode.SUCCESS) return APIErrorType.NONE;
  54. if (sendData === null) return APIErrorType.NONE;
  55. const messageKeys = Object.keys(errors);
  56. if (messageKeys.length === 0) return APIErrorType.SERVER;
  57. if (sendData) {
  58. const sendDataKeys = Object.keys(sendData);
  59. const find = messageKeys.find((key: string) => {
  60. return sendDataKeys.includes(key);
  61. });
  62. if (find) {
  63. return APIErrorType.INPUT;
  64. }
  65. }
  66. return APIErrorType.SERVER;
  67. }, [result, errors, validationError, exclusiveError, generalErrorMessage]);
  68. const getSuccessMessageStr = useCallback(
  69. (res: U) => {
  70. if (typeof successMessage === "boolean") {
  71. return successMessage ? "成功しました" : "";
  72. }
  73. return successMessage(res);
  74. },
  75. [successMessage]
  76. );
  77. const getFailedMessageStr = useCallback(
  78. (res: APICommonResponse | null) => {
  79. if (typeof failedMessage === "boolean") {
  80. return failedMessage ? "失敗しました" : "";
  81. }
  82. return failedMessage(res);
  83. },
  84. [failedMessage]
  85. );
  86. const successCallback = (res: U, sendData: T) => {
  87. setResult(ResultCode.SUCCESS);
  88. const message = getSuccessMessageStr(res);
  89. if (message) {
  90. enqueueSnackbar(message);
  91. }
  92. if (onSuccess) {
  93. onSuccess(res, sendData);
  94. }
  95. };
  96. const failedCallback = (res: APICommonResponse | null) => {
  97. if (res?.result === ResultCode.EXCLUSIVE_ERROR) {
  98. setExclusiveError(true);
  99. }
  100. if (res) {
  101. setResult(res.result);
  102. }
  103. if (res?.messages.errors) {
  104. console.log("seterrors", res.messages.errors);
  105. setErrors(res.messages.errors);
  106. }
  107. if (res?.messages.general) {
  108. setGeneralErrorMessage(res.messages.general);
  109. }
  110. const message = getFailedMessageStr(res);
  111. if (message) {
  112. enqueueSnackbar(message, { variant: "error" });
  113. }
  114. if (onFailed) {
  115. onFailed(res);
  116. }
  117. };
  118. const handleValidationError = (error: any) => {
  119. console.log(error);
  120. setValidationError(true);
  121. };
  122. const callAPI = async (sendData: T) => {
  123. if (form) {
  124. form.clearErrors();
  125. }
  126. clearErrors();
  127. setSendData(sendData);
  128. const res = await apiRequest({
  129. apiMethod,
  130. onSuccess: successCallback,
  131. onFailed: failedCallback,
  132. sendData,
  133. setSending,
  134. errorSetter: form?.setError,
  135. });
  136. return res;
  137. };
  138. const makeSendData = (data: T) => {
  139. return data;
  140. };
  141. return {
  142. callAPI,
  143. sending,
  144. errorMode,
  145. generalErrorMessage,
  146. handleValidationError,
  147. makeSendData,
  148. };
  149. }