Next.js使用SWR过滤博客数据时的两类异常问题排查
问题原因分析
切换标签后列表重置的原因
useSWR默认开启revalidateOnFocus配置,页面重新获得焦点时会自动用初始注册的请求key重拉数据。你当前的实现里,useSWR初始化的请求没有绑定选中的分类参数,过滤操作是脱离SWR手动写fetch、手动更新列表状态,和SWR自身的请求逻辑完全脱节。切回标签时SWR自动发起不带分类参数的请求,拿到全量数据后直接覆盖了你手动更新的列表,就会出现筛选状态重置的问题。
点击Blog链接不重置列表的原因
你做筛选跳转时用了shallow: true配置,不会触发页面组件的重新挂载。同时导航栏的Blog链接指向纯/blog路径时,Next.js会判定当前已经处于/blog路由下,不会主动重置组件状态,而你也没有写监听路由query变化的逻辑——当路由回到不带category参数的/blog路径时,没有代码触发清空筛选、拉取全量文章的操作,列表自然会停留在之前的筛选状态。
代码实现的核心错误
- 违反useSWR的基本使用规则:动态请求参数(分类值)必须纳入SWR的缓存key,你脱离SWR手动维护请求和列表状态,直接导致SWR自带的焦点重验证、网络重连重验证逻辑和本地状态脱节。
- 双状态源不同步:你同时维护了本地组件状态(博客列表、筛选值)和URL query两个状态源,没有指定唯一可信数据源,两边数据不一致必然触发异常。
- shallow路由使用逻辑缺失:用shallow模式更新了URL参数,却没有配套处理路由参数变化时的数据拉取、状态重置逻辑。
修正后的实现方案
以URL query作为筛选状态的唯一可信来源,把分类参数直接绑定到SWR的请求key上,去掉冗余的手动fetch和手动状态更新:
import { useRouter } from 'next/router' import useSWR from 'swr' // 统一封装请求方法 const fetcher = (url) => fetch(url).then(res => res.json()) export default function BlogPage() { const router = useRouter() // 直接从URL读取当前分类,无参数时默认拉全量 const currentCategory = router.query.category || '' // 分类参数纳入SWR key,参数变化时SWR自动发新请求 const { data, isLoading } = useSWR( `${fetch_url}?category=${currentCategory}`, fetcher, { revalidateOnFocus: true } // 开焦点重验证也不会拉错数据 ) const blogItems = data?.blog_posts || [] const nextPgLink = data?.next_pg_link || null // 筛选操作只做路由跳转,不手动发请求、不手动改列表状态 const filterPosts = (category) => { const targetPath = category ? `/blog?category=${category}` : '/blog' router.push(targetPath, undefined, { shallow: true }) } return ( <div> {/* 下拉筛选控件 */} <select value={currentCategory} onChange={(e) => filterPosts(e.target.value)} > <option value="">全部分类</option> <option value="frontend">前端</option> <option value="backend">后端</option> <option value="life">生活</option> </select> {/* 文章列表渲染 */} {isLoading ? <div>加载中...</div> : blogItems.map(post => <div key={post.id}>{post.title}</div>) } </div> ) }
该实现下不需要额外写重置逻辑:点导航栏
/blog链接时URL的category参数被清空,SWR会自动拉取全量文章;切回标签触发重验证时,也会用当前URL上的分类参数发请求,不会出现状态错乱。
内容的提问来源于stack exchange,提问作者Kofi Talent
相关产品推荐
相关产品推荐

