You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Next.js+Strapi项目中能否用useState实现两类演出数据筛选?

方案解答

完全可以用useState实现筛选功能,这是轻量场景下最直接的实现方案,你现有的思路方向是对的,但存在两个可以优化的问题:

  1. 状态重复存储:你在SubMenu内部的state里硬存了两个分类对象,其实props已经传入了全量categories,重复存储会有数据不一致的隐患,而且硬取下标[0]/[1]的写法在后续分类数量变更时会直接报错
  2. 状态作用域不对:筛选状态需要和演出列表组件共享,不能只存在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:36:04