Next.js+Shopify多URL参数产品过滤技术问题求助
解决Next.js + Shopify商品筛选的两个核心问题
问题1:传递多个同键URL参数
要实现多个productType参数在URL中传递,需将router.query.productType设为数组类型,而非单个字符串。同时要动态收集所有选中的复选框值,更新这个数组:
修改ProductFilter组件的关键逻辑
- 修复事件参数缺失问题,在处理函数中传入
event - 收集所有选中的
productType值,更新router.query的数组 - 处理复选框的选中状态回显,匹配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组件的过滤逻辑
- 不直接修改props中的
products,创建副本进行过滤 - 合并价格过滤条件,同时处理min和max
- 兼容
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
相关产品推荐
相关产品推荐

