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

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是否存在,可能导致匹配异常

修复方案

  1. 统一状态字符串:修改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>
  1. 修正初始状态:将subCategoryProducts的初始值改为空数组,避免空对象干扰:
// ProductListSubCategory.jsx中修改初始状态
const [subCategoryProducts, setSubCategoryProducts] = useState([]);
  1. 优化筛选逻辑:在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:18:15