feat: refetch latest data

This commit is contained in:
Ou 2024-09-30 01:51:28 +08:00
parent 26c1c7cf92
commit 7bd0b762b5
6 changed files with 79 additions and 21 deletions

View File

@ -1,14 +1,28 @@
import { atom } from "jotai"
import type { SectionID, SourceID } from "@shared/types"
import { metadata, sourceList } from "@shared/data"
import { atomWithLocalStorage } from "./utils/atom"
import { atomWithLocalStorage } from "./hooks/atomWithLocalStorage"
export const focusSourcesAtom = atomWithLocalStorage<SourceID[]>("focusSources", [], (stored) => {
return stored.filter(item => item in sourceList)
})
const currentSectionIDAtom = atom<SectionID>("focus")
function initRefetchSource() {
let time = 0
// useOnReload
// 没有放在 useOnReload 里面, 可以避免初始化后再修改 refetchSourceAtom导致多次请求 API
const _ = localStorage.getItem("quitTime")
const now = Date.now()
const quitTime = _ ? Number(_) : 0
if (!Number.isNaN(quitTime) && now - quitTime < 1000) {
time = now
}
return Object.fromEntries(Object.keys(sourceList).map(k => [k, time])) as Record<SourceID, number>
}
export const refetchSourceAtom = atom(initRefetchSource())
const currentSectionIDAtom = atom<SectionID>("focus")
export const currentSectionAtom = atom((get) => {
const id = get(currentSectionIDAtom)
if (id === "focus") {

View File

@ -1,11 +1,9 @@
import { Link } from "@tanstack/react-router"
import { useCallback } from "react"
import { useAtomValue } from "jotai"
import type { SourceID } from "@shared/types"
import { queryClient } from "~/main"
import { useAtomValue, useSetAtom } from "jotai"
import logo from "~/assets/react.svg"
import { useDark } from "~/hooks/useDark"
import { currentSectionAtom } from "~/atoms"
import { currentSectionAtom, refetchSourceAtom } from "~/atoms"
function ThemeToggle() {
const { toggleDark } = useDark()
@ -21,13 +19,14 @@ function ThemeToggle() {
function RefreshButton() {
const currentSection = useAtomValue(currentSectionAtom)
const refreshAll = useCallback(async () => {
await queryClient.refetchQueries({
predicate(query) {
return currentSection.sourceList.includes(query.queryKey[0] as SourceID)
},
})
}, [currentSection])
const setRefetchSource = useSetAtom(refetchSourceAtom)
const refreshAll = useCallback(() => {
const obj = Object.fromEntries(currentSection.sourceList.map(id => [id, Date.now()]))
setRefetchSource(prev => ({
...prev,
...obj,
}))
}, [currentSection, setRefetchSource])
return (
<button type="button" className="i-ph:arrow-clockwise btn-pure" onClick={refreshAll} />

View File

@ -5,7 +5,7 @@ import clsx from "clsx"
import { useInView } from "react-intersection-observer"
import { useAtom, useAtomValue } from "jotai"
import { useCallback } from "react"
import { currentSectionAtom, focusSourcesAtom } from "~/atoms"
import { currentSectionAtom, focusSourcesAtom, refetchSourceAtom } from "~/atoms"
export function Main() {
const currentSection = useAtomValue(currentSectionAtom)
@ -40,15 +40,32 @@ function NewsCard({ id, inView }: { id: SourceID, inView: boolean }) {
const addFocusList = useCallback(() => {
setFocusSources(focusSources.includes(id) ? focusSources.filter(i => i !== id) : [...focusSources, id])
}, [setFocusSources, focusSources, id])
const { isPending, error, isFetching, data, refetch } = useQuery({
queryKey: [id],
queryFn: async () => {
const response = await fetch(`/api/${id}?latest`)
const [refetchSource, setRefetchSource] = useAtom(refetchSourceAtom)
const { isPending, error, isFetching, data } = useQuery({
queryKey: [id, refetchSource[id]],
queryFn: async ({ queryKey }) => {
const [_id, _refetchTime] = queryKey as [SourceID, number]
let url = `/api/${_id}`
if (Date.now() - _refetchTime < 1000) {
url = `/api/${_id}?latest`
}
const response = await fetch(url)
return await response.json() as SourceInfo
},
// refetch 时显示原有的数据
placeholderData: prev => prev,
staleTime: 1000 * 60 * 5,
enabled: inView,
refetchOnWindowFocus: false,
refetchOnWindowFocus: true,
})
const manualRefetch = useCallback(() => {
setRefetchSource(prev => ({
...prev,
[id]: Date.now(),
}))
}, [setRefetchSource, id])
if (isPending || !data) {
return (
<>
@ -84,8 +101,14 @@ function NewsCard({ id, inView }: { id: SourceID, inView: boolean }) {
<span>
{formatTime(data!.updateTime)}
</span>
<button type="button" className={clsx(focusSources.includes(id) ? "i-ph:star-fill" : "i-ph:star")} onClick={addFocusList} />
<button type="button" className={clsx("i-ph:arrow-clockwise", isFetching && "animate-spin")} onClick={() => refetch()} />
<div className="flex gap-1">
<button
type="button"
className={clsx("i-ph:arrow-clockwise", isFetching && "animate-spin")}
onClick={manualRefetch}
/>
<button type="button" className={clsx(focusSources.includes(id) ? "i-ph:star-fill" : "i-ph:star")} onClick={addFocusList} />
</div>
</div>
</>
)

20
src/hooks/useOnReload.ts Normal file
View File

@ -0,0 +1,20 @@
import { useEffect } from "react"
import { useBeforeUnload } from "react-use"
export function useOnReload(fn?: () => Promise<void> | void, fallback?: () => Promise<void> | void) {
useBeforeUnload(() => {
localStorage.setItem("quitTime", Date.now().toString())
return false
})
useEffect(() => {
const _ = localStorage.getItem("quitTime")
const quitTime = _ ? Number(_) : 0
if (!Number.isNaN(quitTime) && Date.now() - quitTime < 1000) {
fn?.()
} else {
fallback?.()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}

View File

@ -6,6 +6,7 @@ import "@unocss/reset/tailwind.css"
import "virtual:uno.css"
import type { QueryClient } from "@tanstack/react-query"
import { Header } from "~/components/Header"
import { useOnReload } from "~/hooks/useOnReload"
export const Route = createRootRouteWithContext<{
queryClient: QueryClient
@ -21,6 +22,7 @@ export const Route = createRootRouteWithContext<{
})
export function RootComponent() {
useOnReload()
return (
<div className="p-10">
<Header />