UNPKG

@duongtrungnguyen/next-helper

Version:
493 lines 16.3 kB
"use strict"; "use client"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var hooks_exports = {}; __export(hooks_exports, { useInfiniteQuery: () => useInfiniteQuery, useIsFetching: () => useIsFetching, useMutation: () => useMutation, usePaginatedQuery: () => usePaginatedQuery, useQuery: () => useQuery, useQueryClient: () => useQueryClient }); module.exports = __toCommonJS(hooks_exports); var import_react = require("react"); var import_query_client = require("./query-client"); var import_cache = require("./cache"); const retryFn = async (fn, retries, retryDelay) => { let attempts = 0; const execute = async () => { try { return await fn(); } catch (error) { const maxRetries = typeof retries === "number" ? retries : retries === true ? 3 : 0; if (attempts >= maxRetries) { throw error; } attempts++; const delay = typeof retryDelay === "function" ? retryDelay(attempts) : typeof retryDelay === "number" ? retryDelay : Math.min(1e3 * 2 ** attempts, 3e4); await new Promise((resolve) => setTimeout(resolve, delay)); return execute(); } }; return execute(); }; function useQuery(options) { const { queryKey, queryFn, enabled = true, staleTime = 5 * 60 * 1e3, // 5 minutes cacheTime = 30 * 60 * 1e3, // 30 minutes retry = 3, retryDelay = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4), onSuccess, onError, onSettled, initialData, refetchInterval = false, refetchOnWindowFocus = true, refetchOnMount = true, refetchOnReconnect = true, select = (data2) => data2 } = options; const [data, setData] = (0, import_react.useState)(() => { const cachedData = import_cache.queryCache.get(queryKey); if (cachedData !== void 0) { return select(cachedData); } if (initialData) { const initialValue = typeof initialData === "function" ? initialData() : initialData; if (initialValue !== void 0) { import_cache.queryCache.set(queryKey, initialValue, staleTime, cacheTime); return select(initialValue); } } return void 0; }); const [error, setError] = (0, import_react.useState)(null); const [status, setStatus] = (0, import_react.useState)( data !== void 0 ? "success" : "idle" ); const [isFetching, setIsFetching] = (0, import_react.useState)(false); const refetchIntervalRef = (0, import_react.useRef)(null); const abortControllerRef = (0, import_react.useRef)(null); const mountedRef = (0, import_react.useRef)(true); const fetchData = (0, import_react.useCallback)(async () => { if (!enabled) { return data; } setIsFetching(true); import_query_client.queryClient.setFetching(queryKey, true); if (status === "idle") { setStatus("loading"); } abortControllerRef.current = new AbortController(); try { const result = await retryFn( () => { var _a; return queryFn({}, { queryKey, signal: (_a = abortControllerRef.current) == null ? void 0 : _a.signal }); }, retry, retryDelay ); if (!mountedRef.current) return result; import_cache.queryCache.set(queryKey, result, staleTime, cacheTime); const selectedData = select(result); setData(selectedData); setError(null); setStatus("success"); onSuccess == null ? void 0 : onSuccess(selectedData); onSettled == null ? void 0 : onSettled(selectedData, null); return selectedData; } catch (err) { if (!mountedRef.current) throw err; const error2 = err instanceof Error ? err : new Error(String(err)); setError(error2); setStatus("error"); onError == null ? void 0 : onError(error2); onSettled == null ? void 0 : onSettled(void 0, error2); throw error2; } finally { if (mountedRef.current) { setIsFetching(false); import_query_client.queryClient.setFetching(queryKey, false); } } }, [ queryKey, queryFn, enabled, staleTime, cacheTime, retry, retryDelay, onSuccess, onError, onSettled, select, status, data ]); (0, import_react.useEffect)(() => { const unsubscribe = import_cache.queryCache.subscribe(queryKey, () => { const cachedData = import_cache.queryCache.get(queryKey); if (cachedData !== void 0) { setData(select(cachedData)); setError(null); setStatus("success"); } }); return unsubscribe; }, [queryKey, select]); (0, import_react.useEffect)(() => { mountedRef.current = true; if (enabled && (refetchOnMount || import_cache.queryCache.isStale(queryKey))) { fetchData().catch(() => { }); } return () => { mountedRef.current = false; if (refetchIntervalRef.current) { clearInterval(refetchIntervalRef.current); } if (abortControllerRef.current) { abortControllerRef.current.abort(); } }; }, [enabled, fetchData, queryKey, refetchOnMount]); (0, import_react.useEffect)(() => { if (!refetchInterval || !enabled) { return; } refetchIntervalRef.current = setInterval(() => { if (document.visibilityState === "visible") { fetchData().catch(() => { }); } }, refetchInterval); return () => { if (refetchIntervalRef.current) { clearInterval(refetchIntervalRef.current); } }; }, [refetchInterval, fetchData, enabled]); (0, import_react.useEffect)(() => { if (!refetchOnWindowFocus || !enabled) { return; } const handleFocus = () => { if (import_cache.queryCache.isStale(queryKey)) { fetchData().catch(() => { }); } }; window.addEventListener("focus", handleFocus); return () => { window.removeEventListener("focus", handleFocus); }; }, [refetchOnWindowFocus, fetchData, queryKey, enabled]); (0, import_react.useEffect)(() => { if (!refetchOnReconnect || !enabled) { return; } const handleOnline = () => { fetchData().catch(() => { }); }; window.addEventListener("online", handleOnline); return () => { window.removeEventListener("online", handleOnline); }; }, [refetchOnReconnect, fetchData, enabled]); return { data, error, isLoading: status === "loading", isError: status === "error", isSuccess: status === "success", isFetching, refetch: fetchData, status }; } function useMutation(options) { const { mutationFn, onMutate, onSuccess, onError, onSettled, retry = 0, retryDelay = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4) } = options; const [data, setData] = (0, import_react.useState)(void 0); const [error, setError] = (0, import_react.useState)(null); const [status, setStatus] = (0, import_react.useState)("idle"); const reset = (0, import_react.useCallback)(() => { setData(void 0); setError(null); setStatus("idle"); }, []); const mutate = (0, import_react.useCallback)( async (variables) => { setStatus("loading"); setData(void 0); setError(null); let context; try { if (onMutate) { context = await onMutate(variables); } const result = await retryFn(() => mutationFn(variables), retry, retryDelay); setData(result); setStatus("success"); if (onSuccess) { await onSuccess(result, variables, context); } if (onSettled) { await onSettled(result, null, variables, context); } return result; } catch (err) { const error2 = err instanceof Error ? err : new Error(String(err)); setError(error2); setStatus("error"); if (onError) { await onError(error2, variables, context); } if (onSettled) { await onSettled(void 0, error2, variables, context); } throw error2; } }, [mutationFn, onMutate, onSuccess, onError, onSettled, retry, retryDelay] ); return { data, error, isLoading: status === "loading", isError: status === "error", isSuccess: status === "success", isIdle: status === "idle", reset, mutate, mutateAsync: mutate, status }; } function useInfiniteQuery(options) { const { queryKey, queryFn, getNextPageParam, getPreviousPageParam, initialPageParam = 0, ...queryOptions } = options; const [pages, setPages] = (0, import_react.useState)([]); const [pageParams, setPageParams] = (0, import_react.useState)([initialPageParam]); const [isFetchingNextPage, setIsFetchingNextPage] = (0, import_react.useState)(false); const [isFetchingPreviousPage, setIsFetchingPreviousPage] = (0, import_react.useState)(false); const getPageQueryKey = (pageParam) => [...queryKey, { page: pageParam }]; const queryResult = useQuery({ ...queryOptions, queryKey: getPageQueryKey(initialPageParam), queryFn: async (params, context) => { const result = await queryFn({ ...params, pageParam: initialPageParam }, context); return result; }, onSuccess: (data) => { var _a; setPages([data]); (_a = queryOptions.onSuccess) == null ? void 0 : _a.call(queryOptions, data); } }); const { refetch, ...rest } = queryResult; const hasNextPage = pages.length > 0 && getNextPageParam(pages[pages.length - 1], pages) !== void 0; const hasPreviousPage = pages.length > 0 && (getPreviousPageParam == null ? void 0 : getPreviousPageParam(pages[0], pages)) !== void 0; const fetchNextPage = (0, import_react.useCallback)(async () => { if (!hasNextPage || isFetchingNextPage) { return pages; } setIsFetchingNextPage(true); try { const lastPage = pages[pages.length - 1]; const nextPageParam = getNextPageParam(lastPage, pages); if (nextPageParam === void 0) { return pages; } const pageQueryKey = getPageQueryKey(nextPageParam); const cachedData = import_cache.queryCache.get(pageQueryKey); if (cachedData) { setPages([...pages, cachedData]); setPageParams([...pageParams, nextPageParam]); return [...pages, cachedData]; } const newPageData = await queryFn({ pageParam: nextPageParam }, { queryKey: pageQueryKey }); import_cache.queryCache.set(pageQueryKey, newPageData); const newPages = [...pages, newPageData]; setPages(newPages); setPageParams([...pageParams, nextPageParam]); return newPages; } finally { setIsFetchingNextPage(false); } }, [hasNextPage, isFetchingNextPage, pages, getNextPageParam, pageParams, queryFn, getPageQueryKey]); const fetchPreviousPage = (0, import_react.useCallback)(async () => { if (!hasPreviousPage || isFetchingPreviousPage || !getPreviousPageParam) { return pages; } setIsFetchingPreviousPage(true); try { const firstPage = pages[0]; const previousPageParam = getPreviousPageParam(firstPage, pages); if (previousPageParam === void 0) { return pages; } const pageQueryKey = getPageQueryKey(previousPageParam); const cachedData = import_cache.queryCache.get(pageQueryKey); if (cachedData) { setPages([cachedData, ...pages]); setPageParams([previousPageParam, ...pageParams]); return [cachedData, ...pages]; } const newPageData = await queryFn({ pageParam: previousPageParam }, { queryKey: pageQueryKey }); import_cache.queryCache.set(pageQueryKey, newPageData); const newPages = [newPageData, ...pages]; setPages(newPages); setPageParams([previousPageParam, ...pageParams]); return newPages; } finally { setIsFetchingPreviousPage(false); } }, [hasPreviousPage, isFetchingPreviousPage, pages, getPreviousPageParam, pageParams, queryFn, getPageQueryKey]); const refetchAll = (0, import_react.useCallback)(async () => { const newPages = []; for (let i = 0; i < pageParams.length; i++) { const pageParam = pageParams[i]; const pageQueryKey = getPageQueryKey(pageParam); import_cache.queryCache.invalidate(pageQueryKey); const newPageData = await queryFn({ pageParam }, { queryKey: pageQueryKey }); import_cache.queryCache.set(pageQueryKey, newPageData); newPages.push(newPageData); } setPages(newPages); return newPages; }, [pageParams, queryFn]); return { ...rest, data: pages.length > 0 ? pages : void 0, fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, isFetchingPreviousPage, refetch: refetchAll }; } function usePaginatedQuery(options) { var _a, _b, _c; const { queryKey, queryFn, pageSize = 10, pageIndex: initialPageIndex = 0, keepPreviousData = true, ...queryOptions } = options; const [pageIndex, setPageIndex] = (0, import_react.useState)(initialPageIndex); const paginatedQueryKey = [...queryKey, { pageIndex, pageSize }]; const previousDataRef = (0, import_react.useRef)(null); const queryResult = useQuery({ ...queryOptions, queryKey: paginatedQueryKey, queryFn: async (params, context) => { const combinedParams = { ...params, pagination: { pageIndex, pageSize }, queryKey: context.queryKey, signal: context.signal }; return queryFn(combinedParams); } }); (0, import_react.useEffect)(() => { if (queryResult.data && !queryResult.isLoading) { previousDataRef.current = queryResult.data; } }, [queryResult.data, queryResult.isLoading]); const data = queryResult.isLoading && keepPreviousData && previousDataRef.current ? previousDataRef.current.data : (_a = queryResult.data) == null ? void 0 : _a.data; const totalCount = queryResult.isLoading && keepPreviousData && previousDataRef.current ? previousDataRef.current.totalCount : ((_b = queryResult.data) == null ? void 0 : _b.totalCount) || 0; const pageCount = queryResult.isLoading && keepPreviousData && previousDataRef.current ? previousDataRef.current.pageCount : ((_c = queryResult.data) == null ? void 0 : _c.pageCount) || 0; const canPreviousPage = pageIndex > 0; const canNextPage = pageIndex < pageCount - 1; const previousPage = (0, import_react.useCallback)(() => { setPageIndex((old) => Math.max(0, old - 1)); }, []); const nextPage = (0, import_react.useCallback)(() => { setPageIndex((old) => Math.min(pageCount - 1, old + 1)); }, [pageCount]); return { ...queryResult, data, totalCount, pageCount, pageIndex, pageSize, setPageIndex, previousPage, nextPage, canPreviousPage, canNextPage }; } function useQueryClient() { return import_query_client.queryClient; } function useIsFetching() { const [count, setCount] = (0, import_react.useState)(import_query_client.queryClient.isFetching()); (0, import_react.useEffect)(() => { const interval = setInterval(() => { setCount(import_query_client.queryClient.isFetching()); }, 100); return () => clearInterval(interval); }, []); return count; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useInfiniteQuery, useIsFetching, useMutation, usePaginatedQuery, useQuery, useQueryClient }); //# sourceMappingURL=hooks.js.map