在Next.js+Strapi项目中能否用useState实现两类演出数据筛选?
方案解答
完全可以用useState实现筛选功能,这是轻量场景下最直接的实现方案,你现有的思路方向是对的,但存在两个可以优化的问题:
- 状态重复存储:你在SubMenu内部的state里硬存了两个分类对象,其实props已经传入了全量categories,重复存储会有数据不一致的隐患,而且硬取下标
[0]/[1]的写法在后续分类数量变更时会直接报错 - 状态作用域不对:筛选状态需要和演出列表组件共享,不能只存在SubMenu内部,要么提升到公共父组件,要么使用更适配Next.js的URL参数方案
方案1:轻量useState实现(适合不需要持久化筛选状态的场景)
第一步:状态提升到演出列表页面
// pages/spectacles.js import { useState } from 'react' import Nav from '../components/Nav' import SpectacleList from '../components/SpectacleList' const SpectaclesPage = ({ allSpectacles }) => { // 仅存储当前选中分类的slug,默认null为展示全部 const [activeCategorySlug, setActiveCategorySlug] = useState(null) // 基于选中分类过滤演出数据 const filteredSpectacles = activeCategorySlug ? allSpectacles.filter(spectacle => spectacle.categories.some(cat => cat.slug === activeCategorySlug) ) : allSpectacles // 全量分类去重,避免重复渲染相同分类选项 const uniqueCategories = [...new Map(allSpectacles.flatMap(s => s.categories).map(cat => [cat.id, cat])).values()] return ( <div> <Nav categories={uniqueCategories} activeCategorySlug={activeCategorySlug} onCategoryChange={setActiveCategorySlug} /> <SpectacleList spectacles={filteredSpectacles} /> </div> ) } // Next.js静态数据拉取,从Strapi获取全量演出数据 export async function getStaticProps() { const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_URL}/spectacles`) const allSpectacles = await res.json() return { props: { allSpectacles }, revalidate: 10 } } export default SpectaclesPage
第二步:改写SubMenu组件
// components/SubMenu.js const SubMenu = ({ categories, activeCategorySlug, onCategoryChange }) => { return ( <ul className="sous-menu"> {/* 新增全部选项 */} <li> <a className={!activeCategorySlug ? 'active' : ''} onClick={() => onCategoryChange(null)} > 全部演出 </a> </li> {categories.map((cat) => ( <li key={cat.id}> <a className={cat.slug === activeCategorySlug ? 'active' : ''} onClick={() => onCategoryChange(cat.slug)} > {cat.name} </a> </li> ))} </ul> ) } export default SubMenu
第三步:Nav组件透传参数
// components/Nav.js const Nav = ({ categories, activeCategorySlug, onCategoryChange }) => { return ( <Navbar> <Navbar.Collapse id="responsive-navbar-nav"> <ul className="menu"> <li > <ActiveLink activeClassName="active" as="/spectacles" href="/spectacles"> <a> Spectacles </a> </ActiveLink> <SubMenu categories={categories} activeCategorySlug={activeCategorySlug} onCategoryChange={onCategoryChange} /> </li> </ul> </Navbar.Collapse> </Navbar> ) } export default Nav
方案2:更优的URL参数实现(适配Next.js特性,推荐使用)
这个方案的优势是:
- 刷新页面筛选状态不会丢失
- 可以直接分享筛选后的链接给其他用户
- 浏览器前进后退可以正常切换筛选状态
- 对SEO更友好,不同分类的列表可以被搜索引擎收录
仅需要替换演出页面的状态逻辑即可,SubMenu和Nav组件可以完全复用方案1的代码:
// pages/spectacles.js import { useRouter } from 'next/router' import Nav from '../components/Nav' import SpectacleList from '../components/SpectacleList' const SpectaclesPage = ({ allSpectacles }) => { const router = useRouter() // 从路由query中取分类参数 const activeCategorySlug = router.query.category || null // 过滤逻辑和方案1一致 const filteredSpectacles = activeCategorySlug ? allSpectacles.filter(spectacle => spectacle.categories.some(cat => cat.slug === activeCategorySlug) ) : allSpectacles const uniqueCategories = [...new Map(allSpectacles.flatMap(s => s.categories).map(cat => [cat.id, cat])).values()] // 切换分类时修改路由参数 const handleCategoryChange = (slug) => { router.push({ pathname: '/spectacles', query: slug ? { category: slug } : {} }, undefined, { shallow: true }) // shallow路由不会重新触发数据拉取,性能更好 } return ( <div> <Nav categories={uniqueCategories} activeCategorySlug={activeCategorySlug} onCategoryChange={handleCategoryChange} /> <SpectacleList spectacles={filteredSpectacles} /> </div> ) } // getStaticProps逻辑和方案1完全一致 export async function getStaticProps() { const res = await fetch(`${process.env.NEXT_PUBLIC_STRAPI_URL}/spectacles`) const allSpectacles = await res.json() return { props: { allSpectacles }, revalidate: 10 } } export default SpectaclesPage
内容的提问来源于stack exchange,提问作者Rom-888
相关产品推荐
相关产品推荐

