Next.js Router Cache缓存页面切换时能否保持React Context存活?
问题分析与解决办法
首先明确:Next.js的Router Cache缓存的是服务器组件(RSC)的输出和props,但客户端组件的状态(包括React Context)是存储在浏览器内存中的。当你切换路由时,原页面的客户端组件树会被完全卸载,Context自然会被销毁;导航回来时,客户端组件重新挂载,Context会重建,这就是你看到短暂空状态的原因。Router Cache无法保存客户端的Context状态,但可以通过以下几种方式规避空状态问题:
1. 将筛选/排序状态提升到全局客户端组件
把路由相关的筛选、排序状态放到根布局(layout.tsx)中的全局客户端组件里,这样路由切换时这个组件不会被卸载,状态得以保留。需要为不同路由单独存储状态:
// app/layout.tsx 'use client'; import { useState, usePathname } from 'react'; import { SortAndFilterProvider } from '@/components/providers'; export default function RootLayout({ children }) { // 存储各路由的筛选排序状态 const [routeStates, setRouteStates] = useState({ people: { filters: {}, pagination: {}, sort: {} }, businesses: { filters: {}, pagination: {}, sort: {} }, }); const currentPath = usePathname(); // 提取路由标识(如/people → people) const routeKey = currentPath.slice(1) || 'people'; const currentState = routeStates[routeKey]; // 更新当前路由的状态 const updateRouteState = (newState) => { setRouteStates(prev => ({ ...prev, [routeKey]: { ...currentState, ...newState }, })); }; // 传给Provider的状态值 const sortAndFilterValue = { ...currentState, update: updateRouteState, }; return ( <html lang="zh-CN"> <body> <SortAndFilterProvider value={sortAndFilterValue}> {children} </SortAndFilterProvider> </body> </html> ); }
然后修改Content组件,不再在内部创建状态,直接从全局Context获取:
// app/people/content.tsx 'use client'; import { useContext } from 'react'; import { SortAndFilterContext, DataProvider } from '@/components/providers'; import { useFilteredEntities } from '@/hooks'; const Content = ({ entities }) => { const sortAndFilter = useContext(SortAndFilterContext); const { filters, pagination, sort } = sortAndFilter; const searchableFields = ['name', 'role', 'businessName']; const data = useFilteredEntities({ filters, pagination, sort, entities, searchableFields }); return ( <DataProvider value={data}> <SearchPage /> </DataProvider> ); };
2. 用缓存库缓存筛选后的数据
使用React Query或SWR这类客户端缓存库,把筛选计算后的结果缓存起来。即使Context重建,也能快速从缓存中读取数据,避免空状态:
// hooks/useFilteredEntities.ts 'use client'; import { useQuery } from '@tanstack/react-query'; export const useFilteredEntities = ({ filters, pagination, sort, entities, searchableFields }) => { // 构建唯一的缓存Key,包含路由、数据标识和筛选参数 const queryKey = [ 'filtered-data', typeof window !== 'undefined' ? window.location.pathname : '', entities.length, // 用数据长度作为简易标识,也可以用数据的最后更新时间 filters, pagination, sort, searchableFields, ]; const { data } = useQuery({ queryKey, queryFn: () => { // 这里放你的筛选、排序、分页逻辑 let result = [...entities]; // 应用筛选规则... // 应用排序规则... // 应用分页规则... return result; }, staleTime: 30000, // 和Router Cache的30秒保持一致 cacheTime: 30000, }); // 缓存未就绪时返回原始数据,避免空状态 return data || entities; };
3. 给Context设置初始默认值
在DataProvider中直接用服务器传来的原始entities作为默认值,确保组件挂载时不会出现空状态:
// app/people/content.tsx const Content = ({ entities }) => { const sortAndFilter = useSortAndFilter(); const { filters, pagination, sort } = sortAndFilter; const searchableFields = ['name', 'role', 'businessName']; const data = useFilteredEntities({ filters, pagination, sort, entities, searchableFields }); return ( <SortAndFilterProvider value={sortAndFilter}> {/* 当data未就绪时,用原始entities兜底 */} <DataProvider value={data || entities}> <SearchPage /> </DataProvider> </SortAndFilterProvider> ); };
内容的提问来源于stack exchange,提问作者jsindos
相关产品推荐
相关产品推荐

