React.js产品按品牌二次筛选失效问题求助附代码
问题分析与解决方案
你遇到的问题非常典型——第一次筛选后原始产品数据被覆盖了!当你调用setProducts(products.filter(...))时,直接把筛选后的子集替换了原有的products状态,第二次切换品牌时,只能基于这个子集再过滤,自然找不到其他品牌的产品了。
修复核心思路
保留一份原始产品数据的副本,每次筛选都基于原始数据来处理,而不是修改原状态,这样就能保证每次筛选的数据源都是完整的。
修改后的完整代码
import React, { useEffect, useState } from 'react' import Loader from './../Loader/Loader' import ProductItem from './ProductItem' function Catalog() { // 新增:专门存储从API获取的完整原始产品列表 const [originalProducts, setOriginalProducts] = useState([]) const [products, setProducts] = useState([]) const [brands, setBrands] = useState([]) const [brand, setBrand] = useState('') useEffect(() => { const proxyUrl = 'https://cors-anywhere.herokuapp.com/' const url = 'https://avtodoka-msk.ru/aimylogic-mission.json' fetch(proxyUrl + url) .then(response => response.json()) .then(fetchedProducts => { // 同时初始化原始数据和展示用数据 setOriginalProducts(fetchedProducts) setProducts(fetchedProducts) // 优化:用flatMap更简洁地收集所有品牌(替代map+push) const allBrands = fetchedProducts.flatMap(product => product.brend) setBrands([...new Set(allBrands)]) }) }, []) function toggleBrand(e) { const selectedBrand = e.target.value setBrand(selectedBrand) // 基于原始数据做筛选 if (!selectedBrand) { // 选择"全部品牌"时重置为完整数据 setProducts(originalProducts) return } setProducts( originalProducts.filter(product => { // 兼容处理:确保product.brend是数组(避免潜在报错) return Array.isArray(product.brend) && product.brend.includes(selectedBrand) }) ) } return ( <div className="container pt-5"> {brands.length ? ( <div className="row"> <div className="col-3"> <select value={brand} onChange={toggleBrand}> <option value={''}>Выбрать бренд</option> {brands.map((brend, index) => ( <option value={brend} key={index}>{brend}</option> ))} </select> </div> </div> ) : null} {products.length ? ( <div className="row"> {products.map(product => ( <ProductItem key={product.id} product={product} /> ))} </div> ) : <Loader />} </div> ) } export default Catalog
额外优化建议
如果想进一步提升性能,可以用useMemo缓存筛选结果,避免每次组件渲染都重复执行过滤逻辑:
const filteredProducts = React.useMemo(() => { if (!brand) return originalProducts return originalProducts.filter(product => Array.isArray(product.brend) && product.brend.includes(brand) ) }, [originalProducts, brand])
之后把渲染部分的products换成filteredProducts,还可以直接去掉products状态,让逻辑更简洁。
内容的提问来源于stack exchange,提问作者Як Цидрак




