React多组件共享状态:复选框筛选子分类产品异常求助
问题描述
在Accordian.jsx中基于Filter.jsx的商品状态复选框筛选子分类产品时,未勾选任何选项时subCategoryProducts加载正常,但勾选**"Out Of Box"**选项后,filteredProducts为空,无对应产品展示。已通过父组件ProductListSubCategory共享productCondition状态,各组件代码如下:
父组件ProductListSubCategory代码
import React, { useState, useEffect } from 'react' import Filter from './Filter' import Accordian from './Accordian' import { useParams } from 'react-router-dom' import axios from 'axios' import { Box } from '@chakra-ui/react' const ProductListSubCategory = () => { const [productCondition, setProductCondition] = useState([]); const handleSetProductCondition = (newProductCondition) => { setProductCondition(newProductCondition); } const {subCategory} = useParams(); const {mainCategory} = useParams(); const [subCategoryLink, setSubCategoryLink] = useState([]); const [mainCategoryLink, setMainCategoryLink] = useState([]); const [subCategoryProducts, setSubCategoryProducts] = useState([{}]); useEffect(() => { const fetchProducts = async () => { try { const res = await axios.get(`http://localhost:3005/product/category/${subCategory}`); const productIds = res.data.products; console.log(productIds); const productRequests = productIds.map(productId => axios.get(`http://localhost:3005/product/${productId}`)); console.log(productRequests) const productResponses = await Promise.all(productRequests); const products = productResponses.map(response => response.data); console.log(products); setSubCategoryProducts(products); } catch (error) { console.error(error); console.error(error?.response?.data ?? error.message); } }; fetchProducts(); }, [subCategory]); useEffect(() => { const fetchSubCategoryById = async () => { try { const response = await axios.get(`http://localhost:3005/category/${subCategory}`) setSubCategoryLink(response.data) } catch (err) { console.error(err) } } fetchSubCategoryById(); },[]) useEffect(() => { const fetchMainCategoryById = async () => { try { const response = await axios.get(`http://localhost:3005/category/main-category/${mainCategory}`) setMainCategoryLink(response.data) console.log(mainCategoryLink) } catch (err) { console.error(err) } } fetchMainCategoryById(); },[]) return ( <> <Box sx={{display:"inline-flex", justifyContent:"center"}}> <Filter productCondition={productCondition || []} setProductCondition={handleSetProductCondition} subCategoryProducts={subCategoryProducts}/> <Accordian subCategoryProducts={subCategoryProducts} productCondition={productCondition || []} subCategoryLink={subCategoryLink} mainCategoryLink={mainCategoryLink} /> </Box> </> ) } export default ProductListSubCategory
Accordian.jsx代码
const Accordian = ({subCategoryProducts, subCategoryLink, mainCategoryLink, productCondition} ) => { const productConditions = subCategoryProducts.map(product => product.condition); console.log(productConditions); const filteredProducts = productCondition.length ? subCategoryProducts.filter(product => productCondition.includes(product.condition)) : subCategoryProducts; {filteredProducts.map((product) => ( <GridItem key={product._id}> <Card id={product._id} image={product.image} title={product.title} price={product.price} description={product.description} location={product.location} condition={product.condition} category={product.category} seller={product.seller} /> </GridItem> ))} }
Filter.jsx代码
const Filter = ({productCondition, setProductCondition, subCategoryProducts, }) => { const handleCheckboxChange = (event) => { const value = event.target.value; const isChecked = event.target.checked; const newProductCondition = isChecked ? [...(productCondition || []), value] : (productCondition || []).filter((condition) => condition !== value); setProductCondition(newProductCondition); }; <Checkbox value="New" mr={3} isChecked={productCondition.includes('New')} onChange={handleCheckboxChange} > New </Checkbox> <Checkbox value="Used" mr={3} isChecked={productCondition.includes('Used')} onChange={handleCheckboxChange} > Used </Checkbox> <Checkbox value="Out Of Box" isChecked={productCondition.includes('Out Of Box')} onChange={handleCheckboxChange} > Out of Box </Checkbox> }
排查分析
- 字符串匹配不严格:Filter中Checkbox的
value为"Out Of Box",但后端返回的product.condition可能是"Out of Box"(注意of的大小写),导致includes匹配失败 - 初始状态污染:
subCategoryProducts初始值设为[{}],空对象的condition为undefined,干扰筛选逻辑 - 数据空值未处理:Accordian的筛选逻辑未判断
product.condition是否存在,可能导致匹配异常
修复方案
- 统一状态字符串:修改Filter中Checkbox的
value,与后端返回的condition字段完全一致(比如后端返回"Out of Box"则同步修改):
// Filter.jsx中调整Out Of Box的Checkbox <Checkbox value="Out of Box" isChecked={productCondition.includes('Out of Box')} onChange={handleCheckboxChange} > Out of Box </Checkbox>
- 修正初始状态:将
subCategoryProducts的初始值改为空数组,避免空对象干扰:
// ProductListSubCategory.jsx中修改初始状态 const [subCategoryProducts, setSubCategoryProducts] = useState([]);
- 优化筛选逻辑:在Accordian中增加空值判断,避免处理未加载完成的数据:
// Accordian.jsx中调整筛选逻辑 const filteredProducts = productCondition.length ? subCategoryProducts.filter(product => product?.condition && productCondition.includes(product.condition)) : subCategoryProducts;
验证步骤
- 勾选"Out of Box"复选框,查看控制台打印的
productConditions是否包含匹配的字符串 - 确认
filteredProducts是否正确筛选出对应状态的产品 - 检查后端返回的
product.condition字段值,确保与前端Checkbox的value完全一致
内容的提问来源于stack exchange,提问作者novicecoder
相关产品推荐
相关产品推荐

