Skip to content

Commit

Permalink
feat: added isStale - isFetched - isSuccess - error - fetchStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedx33 committed Oct 10, 2024
1 parent 4d63c58 commit 6eef4be
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-singers-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"duck-query": minor
---

added isStale - isFetched - isSuccess - error - fetchStatus
14 changes: 13 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Install dependencies
run: npm install

# if i have tests i will uncomment it
# Uncomment if you have tests
# - name: Run tests
# run: npm test

Expand All @@ -35,3 +35,15 @@ jobs:
run: |
npx changeset version # Version packages based on changesets
npm publish # Publish the packages to npm
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHU_TOKEN }}
with:
tag_name: v${{ steps.set_version.outputs.new_version }}
release_name: Release v${{ steps.set_version.outputs.new_version }}
body: |
New release based on changesets.
draft: false
prerelease: false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dist-ssr
.vscode/*
!.vscode/extensions.json
.idea
.note
.DS_Store
*.suo
*.ntvs*
Expand Down
91 changes: 49 additions & 42 deletions packages/duck-query/src/core/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,138 +7,145 @@ type Query<T> = {
refetchInterval?: number;
refetchOnWindowFocus?: boolean;
gcTime?: number;
enabled: boolean
};

type CacheEntry<T> = {
data: T;
timestamp: number;
};

type FetchStatus = "fetching" | "idle" | "paused"

type QueryOptions<T> = {
data: T | null;
isLoading: boolean;
isError: boolean;
error: any;
refetch: () => void;
isStale: boolean
isFetched: boolean
isSuccess: boolean
fetchStatus: FetchStatus
};

// global cache cuz when i add it inside the hook it will rebuild everytime cuz the component destroys every hook when unmount
const cache: Record<string, CacheEntry<unknown>> = {};

// TODO make paused after doing retries

export function useQueryNew<T>({
queryKey,
queryFn,
staleTime = 0,
refetchInterval = 0,
refetchOnWindowFocus = false,
gcTime,
enabled = true
}: Query<T>): QueryOptions<T> {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [error, setError] = useState<any>(null)
const [isStale, setIsStale] = useState<boolean>(false)
const [isFetched, setIsFetched] = useState<boolean>(false)
const [isSuccess, setIsSuccess] = useState<boolean>(false)
const [fetchStatus, setFetchStatus] = useState<FetchStatus>("idle")
const key = JSON.stringify(queryKey);
const intervalRef = useRef<number | null>(null);
const staleCheckRef = useRef<number | null>(null);

const checkStale = () => {
const cachedData = cache[key];

if (cachedData && Date.now() - cachedData.timestamp > staleTime) {
fetcher();
}
};

const clearUnusedCache = (key: string, gcTime: number) => {
if (cache[key]) {
if (Date.now() - cache[key].timestamp > gcTime) {
delete cache[key];
}
if (gcTime && cache[key] && Date.now() - cache[key].timestamp > gcTime) {
delete cache[key];
}
};

const fetcher = async () => {
if (!enabled) return;
setIsLoading(true);
setFetchStatus("fetching")
setIsError(false);
try {
const result = await queryFn();
setIsLoading(false);
cache[key] = {
data: result,
timestamp: Date.now(),
};
cache[key] = { data: result, timestamp: Date.now() };
setData(result);
setIsSuccess(true)
} catch (err) {
console.log(err)
setIsError(true);
throw new Error(err as string);
setIsSuccess(false)
setError(err)
setFetchStatus("fetching")
} finally {
setIsLoading(false);
setIsFetched(true)
setFetchStatus("idle")
}
};

const refetch = () => {
fetcher();
};
const refetch = () => fetcher();

useEffect(() => {
if (refetchOnWindowFocus) {
const handleVisibilityChange = () => {
const cachedData = cache[key];
const isStale = !cachedData || (Date.now() - cachedData.timestamp > staleTime);

if (document.visibilityState === "visible" && isStale) {
fetcher();
}
};

document.addEventListener("visibilitychange", handleVisibilityChange);

return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key, staleTime, queryFn, refetchOnWindowFocus]);
}, [key, staleTime, refetchOnWindowFocus]);

useEffect(() => {
const cachedData = cache[key];
const isStale = cachedData && Date.now() - cachedData.timestamp > staleTime;

clearUnusedCache(key, gcTime as number);
if (gcTime) clearUnusedCache(key, gcTime);

if (staleTime > 0 && staleCheckRef.current === null) {
staleCheckRef.current = window.setInterval(() => {
checkStale();
}, staleTime);
if (staleTime > 0 && !staleCheckRef.current) {
staleCheckRef.current = window.setInterval(checkStale, staleTime);
}

if (refetchInterval !== 0 && intervalRef.current === null) {
intervalRef.current = window.setInterval(() => {
fetcher();
}, refetchInterval);
if (refetchInterval > 0 && !intervalRef.current) {
intervalRef.current = window.setInterval(fetcher, refetchInterval);
}

if (cachedData && !isStale) {
setData(cachedData.data as T);
return;
setIsStale(true)
} else {
fetcher();
setIsStale(false)
}

fetcher();

return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
if (staleCheckRef.current !== null) {
clearInterval(staleCheckRef.current);
}
if (intervalRef.current) clearInterval(intervalRef.current);
if (staleCheckRef.current) clearInterval(staleCheckRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key, staleTime, refetchInterval, gcTime]);



return {
data,
isLoading,
isError,
refetch,
error,
isStale,
isFetched,
isSuccess,
fetchStatus
};
}
}

0 comments on commit 6eef4be

Please sign in to comment.