reactjs 在React中使用复选框进行过滤

uyto3xhc  于 2022-11-29  发布在  React
关注(0)|答案(3)|浏览(244)

当我单击复选框时,过滤器工作,但当我再次单击时,过滤器不工作,并且复选框标记似乎也单独工作。我想在单击时,在复选框工作时,过滤此。需要为单个复选框创建布尔值吗?

const App = () => {

  const [category, setCategory] = useState(["electronics","jewelery","men's clothing","women's clothing"]);
  const [products, setProducts] = useState([
    {"id":1,"title":"Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops","price":109.95,"category":"electronics", "rating":{"rate":3.9,"count":120}},
    {"id":2,"title":"Mens Casual Premium Slim Fit T-Shirts ","price":22.3, "category":"men's clothing","rating":{"rate":4.1,"count":259}},
    {"id":3,"title":"Mens Cotton Jacket","price":55.99, "category":"men's clothing","rating":{"rate":4.7,"count":500}},
    {"id":4,"title":"Womens Dress","price":15.99, "category":"women's clothing" ,"rating":{"rate":2.1,"count":430}},
    {"id":5,"title":"John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet","price":695, "category":"jewelery","rating":{"rate":4.6,"count":400}}
  ])

  let [newProducts, setNewProducts] = useState([])
  let [toCheck, setToCheck] = useState(true);

  const filterProducts = (value) => {
    setToCheck(!toCheck);
    if (toCheck) {
      newProducts = products
    setNewProducts([...newProducts.filter((a) => a.category == value)])
    }
    
  }

  return <div>
    <div className='d-flex justify-content-evenly'>
        {
            category.map((elm, index) => {
                return <div className="form-check ms-2" key={index}>
                    <input className="form-check-input" type="checkbox" value={elm} id="flexCheckDefault" onChange={(e) => filterProducts(e.target.value)}/>
                    <label className="form-check-label" htmlFor="flexCheckDefault">
                        {elm}
                    </label>
                </div>
            })
        }
    </div>
    <div className='d-flex flex-wrap'>
    {
       (newProducts.length == 0 ? products : newProducts).map((prod) => {
        return  <div className='card m-3' style={{ width: "400px" }} key={prod.id}>
        <div className='card-body'>
            <p className='card-text'>Id: {prod.id}</p>
            <h3 className='card-title'>Title: {prod.title}</h3>
            <p className='card-text'>Price: {prod.price}</p>
            <p className='card-text'>Category: {prod.category}</p>
            <p className='card-text'>Rating: {prod.rating.rate}</p>
        </div>
    </div>

      })
    }
    </div>
   </div>
}

谢谢你

bttbmeg0

bttbmeg01#

你可以通过e.target.checked而不是使用状态变量来获得复选框是否被选中。你也不需要附加一个值,你可以直接传递它,这并不重要。
因为你要过滤多个元素,所以这个模式也不起作用。你需要保存所有的过滤器,这样你就可以删除一个,而在复选框未选中时保留其他的。我喜欢使用Set来实现这个目的。然后你就可以过滤该集合上的产品了。
您也不需要为不变的常量使用状态变量。
另外,你的idhtmlFor必须是唯一的,否则点击标签总是会选中第一个框。
堆栈 lightning 战:https://stackblitz.com/edit/react-ts-9a9n67?file=App.tsx

const categories = [
  'electronics',
  'jewelery',
  "men's clothing",
  "women's clothing",
];

const allProducts = [
  {
    id: 1,
    title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops',
    price: 109.95,
    category: 'electronics',
    rating: { rate: 3.9, count: 120 },
  },
  {
    id: 2,
    title: 'Mens Casual Premium Slim Fit T-Shirts ',
    price: 22.3,
    category: "men's clothing",
    rating: { rate: 4.1, count: 259 },
  },
  {
    id: 3,
    title: 'Mens Cotton Jacket',
    price: 55.99,
    category: "men's clothing",
    rating: { rate: 4.7, count: 500 },
  },
  {
    id: 4,
    title: 'Womens Dress',
    price: 15.99,
    category: "women's clothing",
    rating: { rate: 2.1, count: 430 },
  },
  {
    id: 5,
    title:
      "John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet",
    price: 695,
    category: 'jewelery',
    rating: { rate: 4.6, count: 400 },
  },
];

export default function App() {
  let [categoryFilters, setcategoryFilters] = useState(new Set());

  function updateFilters(checked, categoryFilter) {
    if (checked)
      setcategoryFilters((prev) => new Set(prev).add(categoryFilter));
    if (!checked)
      setcategoryFilters((prev) => {
        const next = new Set(prev);
        next.delete(categoryFilter);
        return next;
      });
  }

  const filteredProducts =
    categoryFilters.size === 0
      ? allProducts
      : allProducts.filter((p) => categoryFilters.has(p.category));

  return (
    <div>
      <div className="d-flex justify-content-evenly">
        {categories.map((elm, index) => {
          return (
            <div className="form-check ms-2" key={index}>
              <label className="form-check-label">
                <input
                  className="form-check-input"
                  type="checkbox"
                  onChange={(e) => updateFilters(e.target.checked, elm)}
                />
                {elm}
              </label>
            </div>
          );
        })}
      </div>
      <div className="d-flex flex-wrap">
        {filteredProducts.map((prod) => {
          return (
            <div className="card m-3" style={{ width: '400px' }} key={prod.id}>
              <div className="card-body">
                <p className="card-text">Id: {prod.id}</p>
                <h3 className="card-title">Title: {prod.title}</h3>
                <p className="card-text">Price: {prod.price}</p>
                <p className="card-text">Category: {prod.category}</p>
                <p className="card-text">Rating: {prod.rating.rate}</p>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
mu0hgdu0

mu0hgdu02#

简单解决方案(使用类别数组的更新数据模型)

const [category, setCategory] = useState([{value : "electronics", isChecked: false},{value : "jewelery", isChecked: false},{value : "men's clothing", isChecked: false},{value : "women's clothing", isChecked: false}]);
  const [products, setProducts] = useState([
    {"id":1,"title":"Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops","price":109.95,"category":"electronics", "rating":{"rate":3.9,"count":120}},
    {"id":2,"title":"Mens Casual Premium Slim Fit T-Shirts ","price":22.3, "category":"men's clothing","rating":{"rate":4.1,"count":259}},
    {"id":3,"title":"Mens Cotton Jacket","price":55.99, "category":"men's clothing","rating":{"rate":4.7,"count":500}},
    {"id":4,"title":"Womens Dress","price":15.99, "category":"women's clothing" ,"rating":{"rate":2.1,"count":430}},
    {"id":5,"title":"John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet","price":695, "category":"jewelery","rating":{"rate":4.6,"count":400}}
  ])

  let [newProducts, setNewProducts] = useState([]);

  const filterProducts = (e) => {
    const categories = [...category];
    const cat = categories.filter((cat)=>cat.value ===  e.target.value);
    cat[0].isChecked = e.target.checked;
    setCategory([...categories]);

    const tempProduct = [];
    for(let c of categories){
      if(c.isChecked){
        tempProduct.push(...products.filter((a) => a.category === c.value));
      }
    }

    setNewProducts([...tempProduct]);
  }

  return <div>
    <div className='d-flex justify-content-evenly'>
        {
            category.map((elm, index) => {
                return <div className="form-check ms-2" key={index}>
                    <input className="form-check-input" type="checkbox" value={elm.value} id="flexCheckDefault" onChange={filterProducts}/>
                    <label className="form-check-label" htmlFor="flexCheckDefault">
                        {elm.value}
                    </label>
                </div>
            })
        }
    </div>
    <div className='d-flex flex-wrap'>
    {
       (newProducts.length === 0 ? products : newProducts).map((prod) => {
        return  <div className='card m-3' style={{ width: "400px" }} key={prod.id}>
        <div className='card-body'>
            <p className='card-text'>Id: {prod.id}</p>
            <h3 className='card-title'>Title: {prod.title}</h3>
            <p className='card-text'>Price: {prod.price}</p>
            <p className='card-text'>Category: {prod.category}</p>
            <p className='card-text'>Rating: {prod.rating.rate}</p>
        </div>
    </div>

      })
    }
    </div>
   </div>
mcvgt66p

mcvgt66p3#

以下是一个最小解决方案,它在不使用useEffect或更改categoryproducts的情况下工作。
可以在发布的代码中解决的几个问题:
1.每个input需要有一个唯一的id,它与label上的htmlFor匹配,以便这对input一起工作。
1.当filterProducts设置一个新的状态值时,它不会在同一个块中获得更新的值。仅使用此事件来设置toCheck中的过滤器。

  1. toCheck的实值型别可以是[category]: Boolean的对象,因此可以分别筛选每个分类,也可以组合筛选。
    1.原始的products可以在Map之前被过滤,因此不需要保持newProducts的另一状态,或者修改products数据。
    示例(现场演示:(第10页)
import React, { useState } from 'react';
import './style.css';

export default function App() {
  const [category, setCategory] = useState([
    'electronics',
    'jewelery',
    "men's clothing",
    "women's clothing",
  ]);
  const [products, setProducts] = useState([
    {
      id: 1,
      title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops',
      price: 109.95,
      category: 'electronics',
      rating: { rate: 3.9, count: 120 },
    },
    {
      id: 2,
      title: 'Mens Casual Premium Slim Fit T-Shirts ',
      price: 22.3,
      category: "men's clothing",
      rating: { rate: 4.1, count: 259 },
    },
    {
      id: 3,
      title: 'Mens Cotton Jacket',
      price: 55.99,
      category: "men's clothing",
      rating: { rate: 4.7, count: 500 },
    },
    {
      id: 4,
      title: 'Womens Dress',
      price: 15.99,
      category: "women's clothing",
      rating: { rate: 2.1, count: 430 },
    },
    {
      id: 5,
      title:
        "John Hardy Women's Legends Naga Gold & Silver Dragon Station Chain Bracelet",
      price: 695,
      category: 'jewelery',
      rating: { rate: 4.6, count: 400 },
    },
  ]);

  const [toCheck, setToCheck] = useState({});

  const filterProducts = (value) =>
    setToCheck((prev) => {
      return { ...prev, [value]: !!!prev[value] };
    });

  return (
    <div>
      <div className="d-flex justify-content-evenly">
        {category.map((elm, index) => {
          return (
            <div className="form-check ms-2" key={index}>
              <input
                className="form-check-input"
                type="checkbox"
                value={elm}
                id={`flexCheckDefault-${index}`}
                onChange={(e) => filterProducts(e.target.value)}
              />
              <label
                className="form-check-label"
                htmlFor={`flexCheckDefault-${index}`}
              >
                {elm}
              </label>
            </div>
          );
        })}
      </div>
      <div className="d-flex flex-wrap">
        {products
          .filter((prod) =>
            Object.keys(toCheck).length === 0 ? true : !!toCheck[prod.category]
          )
          .map((prod) => {
            return (
              <div
                className="card m-3"
                style={{ width: '400px' }}
                key={prod.id}
              >
                <div className="card-body">
                  <p className="card-text">Id: {prod.id}</p>
                  <h3 className="card-title">Title: {prod.title}</h3>
                  <p className="card-text">Price: {prod.price}</p>
                  <p className="card-text">Category: {prod.category}</p>
                  <p className="card-text">Rating: {prod.rating.rate}</p>
                </div>
              </div>
            );
          })}
      </div>
    </div>
  );
}

相关问题