React+Next.js搭配Firestore时跳转页面后保留已获取数据避免重复读取的实现方法
实现方案
你可以根据业务场景选择以下几种实现方式,均可以避免重复触发Firestore读请求:
方案1:全局状态管理(内存缓存)
适用场景:仅需要同一次打开页面的会话内复用数据,允许刷新页面后重新请求。
Next.js客户端路由跳转时,全局状态不会随页面组件卸载销毁,只要把拉取到的counts存在全局状态里,返回页面时直接读取已有值即可,无需二次请求。以下用轻量状态库Zustand为例:
// store/countStore.js 全局状态定义 import { create } from 'zustand' const useCountStore = create((set) => ({ counts: null, setCounts: (data) => set({ counts: data }) }))
修改原有页面的逻辑:
import useCountStore from '@/store/countStore' // 页面组件内 const { counts, setCounts } = useCountStore() useEffect(() => { // 已有数据直接跳过请求 if (counts) return const newObj = {}; FB.getAllFiles() .then((snapshot) => { snapshot.forEach((doc) => { const data = doc.data(); newObj[data["name"]] = data["count"]; }); setCounts(newObj); }) .catch((e) => console.log(e)); }, [counts, setCounts]);
你也可以用React Context、Redux等其他全局状态方案实现,逻辑一致。
方案2:持久化本地缓存
适用场景:希望用户刷新页面后也无需重新请求,允许数据有一定的更新延迟。
可以把数据存在localStorage/sessionStorage里,还可以配置过期时间避免数据长期不更新:
const [counts, setCounts] = useState(null) useEffect(() => { // 优先读取本地缓存 const cachedCounts = localStorage.getItem('fileCounts') const cacheExpireTime = localStorage.getItem('fileCountsExpire') // 缓存存在且未过期直接复用 if (cachedCounts && cacheExpireTime && Date.now() < Number(cacheExpireTime)) { setCounts(JSON.parse(cachedCounts)) return } const newObj = {}; FB.getAllFiles() .then((snapshot) => { snapshot.forEach((doc) => { const data = doc.data(); newObj[data["name"]] = data["count"]; }); setCounts(newObj); // 写入缓存,设置1小时过期 localStorage.setItem('fileCounts', JSON.stringify(newObj)) localStorage.setItem('fileCountsExpire', String(Date.now() + 3600 * 1000)) }) .catch((e) => console.log(e)); }, []);
方案3:使用数据请求库内置缓存
适用场景:项目中有大量请求需要统一管理,希望省去手动维护缓存的成本。
TanStack Query(原React Query)、SWR这类数据请求库原生支持请求缓存、新鲜时间配置,还自带错误重试、自动重验证等能力。以下用TanStack Query为例:
首先在项目根组件配置Provider:
// _app.jsx / app/layout.jsx import { QueryClient, QueryClientProvider } from '@tanstack/react-query' const queryClient = new QueryClient() export default function App({ Component, pageProps }) { return ( <QueryClientProvider client={queryClient}> <Component {...pageProps} /> </QueryClientProvider> ) }
页面内使用:
import { useQuery } from '@tanstack/react-query' // 封装请求逻辑 const getFileCounts = async () => { const newObj = {} const snapshot = await FB.getAllFiles() snapshot.forEach((doc) => { const data = doc.data() newObj[data["name"]] = data["count"] }) return newObj } // 页面组件内 const { data: counts, isLoading, error } = useQuery({ queryKey: ['fileCounts'], // 缓存的唯一标识 queryFn: getFileCounts, staleTime: 3600 * 1000, // 1小时内数据视为新鲜,不会重复发起请求 cacheTime: 24 * 3600 * 1000 // 缓存最多保留24小时 })
如果后续有数据更新的需求,可以在更新逻辑中主动触发对应queryKey的缓存失效,即可拉取最新数据。
内容的提问来源于stack exchange,提问作者guckmalmensch
相关产品推荐
相关产品推荐

