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

Next.js+Shopify多URL参数产品过滤技术问题求助

解决Next.js + Shopify商品筛选的两个核心问题

问题1:传递多个同键URL参数

要实现多个productType参数在URL中传递,需将router.query.productType设为数组类型,而非单个字符串。同时要动态收集所有选中的复选框值,更新这个数组:

修改ProductFilter组件的关键逻辑

  1. 修复事件参数缺失问题,在处理函数中传入event
  2. 收集所有选中的productType值,更新router.query的数组
  3. 处理复选框的选中状态回显,匹配URL中的参数

修改后的ProductFilter代码:

import { useState, useEffect } from "react"
import { useRouter } from "next/router"

export default function ProductFilter({ products }) {
  const router = useRouter()
  const { productType, minPrice, maxPrice } = router.query

  // 同步输入框值与URL参数
  const [localMinPrice, setLocalMinPrice] = useState(minPrice || "")
  const [localMaxPrice, setLocalMaxPrice] = useState(maxPrice || "")

  useEffect(() => {
    setLocalMinPrice(minPrice || "")
    setLocalMaxPrice(maxPrice || "")
  }, [minPrice, maxPrice])

  function handleMinimumPriceQuery(event) {
    const value = event.target.value
    setLocalMinPrice(value)
    if (value) {
      router.query.minPrice = value
    } else {
      delete router.query.minPrice
    }
    router.push(router, undefined, { shallow: true })
  }

  function handleMaximumPriceQuery(event) {
    const value = event.target.value
    setLocalMaxPrice(value)
    if (value) {
      router.query.maxPrice = value
    } else {
      delete router.query.maxPrice
    }
    router.push(router, undefined, { shallow: true })
  }

  function handleProductTypeQuery(event) {
    const { value, checked } = event.target
    // 确保productType始终是数组(处理单个参数时的字符串情况)
    let currentTypes = Array.isArray(productType) ? [...productType] : productType ? [productType] : []

    if (checked) {
      if (!currentTypes.includes(value)) {
        currentTypes.push(value)
      }
    } else {
      currentTypes = currentTypes.filter(type => type !== value)
    }

    if (currentTypes.length > 0) {
      router.query.productType = currentTypes
    } else {
      delete router.query.productType
    }
    router.push(router, undefined, { shallow: true })
  }

  // 判断当前productType是否被选中
  const isTypeChecked = (type) => {
    if (!productType) return false
    return Array.isArray(productType) ? productType.includes(type) : productType === type
  }

  const getFilteredProductTypes = products.filter(
    (v, i, a) => a.findIndex((v2) => v2.productType === v.productType) === i
  )

  return (
    <aside>
      <form className="flex flex-col gap-4">
        <div className="grid grid-cols-2 gap-4 pr-4">
          <div className="flex flex-col">
            <label htmlFor="priceMin">Minimum Price</label>
            <input
              className="border p-1 flex flex-1"
              type="number"
              id="priceMin"
              placeholder="$0"
              value={localMinPrice}
              onChange={handleMinimumPriceQuery}
            />
          </div>
          <div className="flex flex-col">
            <label htmlFor="priceMax">Maximum Price</label>
            <input
              className="border p-1 flex flex-1"
              type="number"
              id="priceMax"
              value={localMaxPrice}
              onChange={handleMaximumPriceQuery}
            />
          </div>
        </div>
        <div className="productTypes">
          {getFilteredProductTypes.map((product) => (
            <div key={product.productType} className="flex gap-2">
              <input
                className="checkbox"
                type="checkbox"
                name="productType"
                id={product.productType}
                value={product.productType}
                checked={isTypeChecked(product.productType)}
                onChange={handleProductTypeQuery}
              />
              <label htmlFor={product.productType}>{product.productType}</label>
            </div>
          ))}
        </div>
      </form>
    </aside>
  )
}

问题2:多参数同时执行过滤

当前代码的过滤逻辑分开判断会导致重复过滤或逻辑冲突,需创建原始产品的副本,按顺序应用所有过滤条件,同时处理productType的数组/字符串两种情况:

修改ProductsPage组件的过滤逻辑

  1. 不直接修改props中的products,创建副本进行过滤
  2. 合并价格过滤条件,同时处理min和max
  3. 兼容productType为单个字符串或数组的情况

修改后的ProductsPage代码:

import { shopifyClient, parseShopifyResponse } from "/lib/shopify"
import { useRouter } from "next/router"
import ProductList from "/components/Products/ProductList"
import ProductFilter from "/components/Products/ProductFilter"

export async function getStaticProps() {
  const getAllProducts = await shopifyClient.product.fetchAll()
  const products = parseShopifyResponse(getAllProducts)

  return {
    props: {
      products,
    },
  }
}

export default function ProductsPage({ products }) {
  const router = useRouter()
  const { maxPrice, minPrice, productType } = router.query

  // 创建产品副本,避免修改原始props
  let filteredProducts = [...products]

  // 价格过滤:同时处理min和max
  if (minPrice || maxPrice) {
    filteredProducts = filteredProducts.filter(product => {
      const price = Number(product.variants[0].price.amount)
      const meetsMin = minPrice ? price >= Number(minPrice) : true
      const meetsMax = maxPrice ? price <= Number(maxPrice) : true
      return meetsMin && meetsMax
    })
  }

  // 产品类型过滤:兼容单个值和数组
  if (productType) {
    const targetTypes = Array.isArray(productType) ? productType : [productType]
    filteredProducts = filteredProducts.filter(product => targetTypes.includes(product.productType))
  }

  return (
    <>
      <main className="grid grid-cols-1 lg:grid-cols-5 gap-4">
        <div className="filter">
          <ProductFilter products={products} />
        </div>
        <div className="products col-span-1 lg:col-span-4">
          <ProductList products={filteredProducts} />
        </div>
      </main>
    </>
  )
}

关键优化点说明

  • 使用shallow: true进行路由跳转,避免页面重新获取数据,提升性能
  • 同步输入框和复选框的状态与URL参数,确保刷新页面时过滤状态不丢失
  • 价格过滤合并逻辑,避免重复过滤
  • 兼容productType参数的字符串/数组两种格式,处理单个或多个选中的情况

内容的提问来源于stack exchange,提问作者ElvarP

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 18:05:21