import { useCallback, useMemo, useState } from "react"; import { APICommonResponse, apiRequest, ResultCode } from "api"; import { UseFormReturn } from "react-hook-form"; import { Dictionary } from "@types"; import { useSnackbar } from "notistack"; export const APIErrorType = { NONE: "none", INPUT: "input", SERVER: "server", EXCLUSIVE: "exclusive", } as const; export type APIErrorType = (typeof APIErrorType)[keyof typeof APIErrorType]; export default function useAPICall< T extends object, U extends APICommonResponse >({ apiMethod, onSuccess, onFailed, form, successMessage = false, failedMessage = false, }: { apiMethod: (sendData: T) => Promise; onSuccess?: (res: U, sendData: T) => void; onFailed?: (res: APICommonResponse | null) => void; form?: UseFormReturn; successMessage?: ((res: U) => string) | boolean; failedMessage?: ((res: APICommonResponse | null) => string) | boolean; }) { const [sending, setSending] = useState(false); const [sendData, setSendData] = useState(null); const [result, setResult] = useState(null); const [errors, setErrors] = useState({}); const [validationError, setValidationError] = useState(false); const [exclusiveError, setExclusiveError] = useState(false); const [generalErrorMessage, setGeneralErrorMessage] = useState(""); const { enqueueSnackbar } = useSnackbar(); const clearErrors = () => { setResult(null); setErrors({}); setSendData(null); setValidationError(false); setExclusiveError(false); setGeneralErrorMessage(""); }; // 入力項目に対してエラーレスポンスがあるか、ないかでエラーのタイプを設定する const errorMode = useMemo(() => { if (generalErrorMessage !== "") return APIErrorType.INPUT; if (exclusiveError) return APIErrorType.EXCLUSIVE; if (validationError) return APIErrorType.INPUT; if (result === null) return APIErrorType.NONE; if (result === ResultCode.SUCCESS) return APIErrorType.NONE; if (sendData === null) return APIErrorType.NONE; const messageKeys = Object.keys(errors); if (messageKeys.length === 0) return APIErrorType.SERVER; if (sendData) { const sendDataKeys = Object.keys(sendData); const find = messageKeys.find((key: string) => { return sendDataKeys.includes(key); }); if (find) { return APIErrorType.INPUT; } } return APIErrorType.SERVER; }, [result, errors, validationError, exclusiveError, generalErrorMessage]); const getSuccessMessageStr = useCallback( (res: U) => { if (typeof successMessage === "boolean") { return successMessage ? "成功しました" : ""; } return successMessage(res); }, [successMessage] ); const getFailedMessageStr = useCallback( (res: APICommonResponse | null) => { if (typeof failedMessage === "boolean") { return failedMessage ? "失敗しました" : ""; } return failedMessage(res); }, [failedMessage] ); const successCallback = (res: U, sendData: T) => { setResult(ResultCode.SUCCESS); const message = getSuccessMessageStr(res); if (message) { enqueueSnackbar(message); } if (onSuccess) { onSuccess(res, sendData); } }; const failedCallback = (res: APICommonResponse | null) => { if (res?.result === ResultCode.EXCLUSIVE_ERROR) { setExclusiveError(true); } if (res) { setResult(res.result); } if (res?.messages.errors) { console.log("seterrors", res.messages.errors); setErrors(res.messages.errors); } if (res?.messages.general) { setGeneralErrorMessage(res.messages.general); } const message = getFailedMessageStr(res); if (message) { enqueueSnackbar(message, { variant: "error" }); } if (onFailed) { onFailed(res); } }; const handleValidationError = (error: any) => { console.log(error); setValidationError(true); }; const callAPI = async (sendData: T) => { if (form) { form.clearErrors(); } clearErrors(); setSendData(sendData); const res = await apiRequest({ apiMethod, onSuccess: successCallback, onFailed: failedCallback, sendData, setSending, errorSetter: form?.setError, }); return res; }; const makeSendData = (data: T) => { return data; }; return { callAPI, sending, errorMode, generalErrorMessage, handleValidationError, makeSendData, }; }