PozoAccount/src/hooks/useFetch.js

107 lines
2.7 KiB
JavaScript
Raw Normal View History

2025-12-11 16:13:47 +05:30
import { useState, useEffect, useCallback, useRef } from 'react';
function useFetch(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const abortControllerRef = useRef(null);
const {
method = 'GET',
headers = {},
body = null,
skip = false, // Don't auto-fetch on mount
...restOptions
} = options;
const fetchData = useCallback(async (dynamicBody = null, dynamicOptions = {}) => {
setLoading(true);
setError(null);
// Cancel previous request
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
abortControllerRef.current = new AbortController();
try {
const finalBody = dynamicBody || body;
const finalMethod = dynamicOptions.method || method;
const config = {
method: finalMethod,
headers: {
'Content-Type': 'application/json',
...headers,
...dynamicOptions.headers
},
signal: abortControllerRef.current.signal,
...restOptions,
...dynamicOptions
};
// Add body for POST, PUT, PATCH, DELETE (if needed)
if (finalBody && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(finalMethod)) {
config.body = typeof finalBody === 'string'
? finalBody
: JSON.stringify(finalBody);
}
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Handle empty responses (like 204 No Content)
const contentType = response.headers.get('content-type');
let result = null;
if (contentType && contentType.includes('application/json')) {
result = await response.json();
} else if (response.status !== 204) {
result = await response.text();
}
setData(result);
setError(null);
return result;
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
} else {
setError(err.message);
throw err;
}
} finally {
setLoading(false);
}
}, [url, method, body, JSON.stringify(headers), JSON.stringify(restOptions)]);
useEffect(() => {
if (!skip && method === 'GET') {
fetchData();
}
return () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, [fetchData, skip, method]);
const refetch = useCallback(() => {
return fetchData();
}, [fetchData]);
return {
data,
loading,
error,
refetch,
execute: fetchData // Alias for manual calls
};
}
export default useFetch;