React - 创建自定义过滤器

问题描述 投票:0回答:1

我已经从技术上创建了我想要通过下拉选择字段实现的功能,用于选择用于按类别和品牌过滤产品列表的过滤器。我构建了下面的代码片段,以便您可以看到工作代码的示例(但是我无法让它在代码片段工具中正常运行,尽管我制作的 CodePen 中的代码完全相同:https: //codepen.io/Chobbit/pen/qBzrPLe

const {useState, useEffect}  = React

const productDataList = {
  "products": [
    {
      "id": 1,
      "title": "Essence Mascara Lash Princess",
      "category": "beauty",
      "price": 9.99,
      "brand": "Essence",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png"
    },
    {
      "id": 2,
      "title": "Eyeshadow Palette with Mirror",
      "category": "beauty",
      "price": 19.99,
      "brand": "Glamour Beauty",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png"
    },
    {
      "id": 3,
      "title": "Powder Canister",
      "category": "beauty",
      "price": 14.99,
      "brand": "Velvet Touch",
      "thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png"
    },
    {
      "id": 4,
      "title": "Dior J'adore",
      "category": "fragrances",
      "price": 89.99,
      "brand": "Dior",
      "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png"
    },
    {
      "id": 5,
      "title": "Dolce Shine Eau de",
      "category": "fragrances",
      "price": 69.99,
      "brand": "Dolce & Gabbana",
      "thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png"
    },
    {
      "id": 6,
      "title": "Annibale Colombo Sofa",
      "category": "furniture",
      "price": 2499.99,
      "brand": "Annibale Colombo",
      "thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png"
    },
  ],
  "total": 194,
  "skip": 0,
  "limit": 36
}

const App = () => {

  const [productsLimit, setProductsLimit] = useState(productDataList.limit)
  const [filterData, setFilterData] = useState(productDataList.products || []);
  const [categories, setCategories] = useState([]);
  const [brands, setBrands] = useState([]);

  useEffect(() => {
    let filteredCategories = [],
        filteredBrands = [];
        productDataList.products.forEach((product) => {
      if (!filteredCategories.includes(product.category)) {
        filteredCategories.push(product.category);
      }
      if (!filteredCategories.includes(product.brand)) {
        filteredBrands.indexOf(product.brand) == -1 && product.brand != '' && product.brand != undefined && product.brand != null ? filteredBrands.push(product.brand) : null;
      }
    });
    setCategories(filteredCategories);
    setBrands(filteredBrands);
  }, []);

  const productFilters = (products, filt) => {
    let filteredProducts = [];
    if (categories.includes(filt)) {
      filteredProducts = products.category.toLowerCase() === filt.toLowerCase();
    } else if (brands.includes(filt)) {
      filteredProducts = products.brand === filt;
    }
    return filteredProducts;
  };

  const filter = (byFilter) => {
    if (byFilter) {
      let productsData = [...productDataList.products];
      productsData = productsData.filter((filter) => {
        return productFilters(filter, byFilter);
      });
      setFilterData(productsData);
    } else {
      setFilterData(productDataList.products);
    }
  };

  return (

    <div>
      <select onChange={(e) => filter(e.target.value)}>
        <option value="Filter Categories" selected="selected">Filter by categories</option>
        {categories.map((catFilter) => (
          console.log(catFilter),
          <option value={catFilter}>{catFilter}</option>
        ))}
      </select>
      <select onChange={(e) => filter(e.target.value)}>
        <option value="Filter Categories" selected="selected">Filter by brands</option>
        {brands.map((item) => (
          console.log(item),
          <option value={item}>{item}</option>
        ))}
      </select>
      <button onClick={filter}>Clear filter</button>
    


      <div className='products'>

        {/* checks the product data exists before mapping it */}
        {filterData?.slice(0, productsLimit).map(product => (
          <div className='product' key={product.id}>
            <div className='productImage'>
              <img src={product.thumbnail} />
            </div>
            <div className='productInfo'>
              <div className='productTitle'>
                <div className='productBrand'>{product.brand}</div> 
                <div className='productName'>{product.title}</div>
              </div>
              <div className='productPrice'>Price: £{product.price}</div>
              {}
            </div>

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

// ReactDOM.render(<App />, document.querySelector('#root'));
.products {
    display: flex;
    flex-wrap: wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>

**

我现在想做的是实现相同的过滤结果,但使用用户可以单击的更明显的过滤器列表,而不是下拉菜单。

**

我已经尝试了两种方法,第一种是使用一个函数,为映射的产品数据 json 中找到的每个不同类别/品牌选项创建一个元素,并将它们附加到过滤器字段。第二种方法只是通过 return() JSX 中找到的类别/品牌选项进行映射。这两种方法都会在页面上生成过滤器列表,就像我希望它们与过滤器一起显示以及过滤器将返回的产品数量一样:

Filters appearance on the page

但是我遇到的问题是,当我尝试向创建的每个将调用

onClick
函数的过滤器添加
filter()
时,方法 1 在单击时不会执行任何操作。所以我觉得我没有正确添加这一点
onClick=${(e) => filter(e.target.textContent.replace(/\s+$/, ''))}
来在创建新元素时触发该函数:

dataFilters.innerHTML = Object.entries(numberOfFilters).map(([mapFilterName,numberFound])=>`<div class="filter"><div class="name" onClick=${(e) => filter(e.target.textContent.replace(/\s+$/, ''))}>${mapFilterName} </div> <div class="count"> (${numberFound})</div></div>`).join("\n");

第二种方法,当我在jsx中设置它时,如下所示:

<div className="catagoryFilters f_rmc f_sb">
  <div className="filterTitle">Categories</div>
  <div className="filtersList">
    {categories.map((filter) => (
      <div className="filter">
        <div className="name" onClick={(e) => filter(e.target.textContent.replace(/\s+$/, ''))}>
          {filter} 
        </div> 
        <div className="count"> 
          ({filter.length})
        </div>
      </div>
    ))}
  </div>
</div>

它确实尝试调用

filter()
函数,但我得到的只是此错误消息,我不明白为什么?: Function call error

我已经尝试了一些方法来使其以这种方式工作,尽管使用下拉菜单使其工作,但我显然做了一些我不太理解的错误,在单击过滤器时正确调用

filter()
函数映射列表。

因此,如果您能帮助我完成这项工作并理解我做错了什么,我们将不胜感激,谢谢。

javascript reactjs filter onclicklistener
1个回答
0
投票

检查过滤功能并确保其设置正确,这里有一些示例作为示例代码,

const { useState, useEffect } = React;

const productDataList = {
  products: [
    {
      id: 1,
      title: "Essence Mascara Lash Princess",
      category: "beauty",
      price: 9.99,
      brand: "Essence",
      thumbnail: "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png",
    },
    {
      id: 2,
      title: "Eyeshadow Palette with Mirror",
      category: "beauty",
      price: 19.99,
      brand: "Glamour Beauty",
      thumbnail: "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png",
    },
    {
      id: 3,
      title: "Powder Canister",
      category: "beauty",
      price: 14.99,
      brand: "Velvet Touch",
      thumbnail: "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png",
    },
    {
      id: 4,
      title: "Dior J'adore",
      category: "fragrances",
      price: 89.99,
      brand: "Dior",
      thumbnail: "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png",
    },
    {
      id: 5,
      title: "Dolce Shine Eau de",
      category: "fragrances",
      price: 69.99,
      brand: "Dolce & Gabbana",
      thumbnail: "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png",
    },
    {
      id: 6,
      title: "Annibale Colombo Sofa",
      category: "furniture",
      price: 2499.99,
      brand: "Annibale Colombo",
      thumbnail: "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png",
    },
  ],
  total: 194,
  skip: 0,
  limit: 36,
};

const App = () => {
  const [productsLimit, setProductsLimit] = useState(productDataList.limit);
  const [filterData, setFilterData] = useState(productDataList.products || []);
  const [categories, setCategories] = useState([]);
  const [brands, setBrands] = useState([]);

  useEffect(() => {
    let filteredCategories = [];
    let filteredBrands = [];
    productDataList.products.forEach((product) => {
      if (!filteredCategories.includes(product.category)) {
        filteredCategories.push(product.category);
      }
      if (!filteredBrands.includes(product.brand)) {
        filteredBrands.push(product.brand);
      }
    });
    setCategories(filteredCategories);
    setBrands(filteredBrands);
  }, []);

  const productFilters = (product, filter) => {
    if (categories.includes(filter)) {
      return product.category.toLowerCase() === filter.toLowerCase();
    } else if (brands.includes(filter)) {
      return product.brand === filter;
    }
    return true;
  };

  const filter = (byFilter) => {
    if (byFilter) {
      let productsData = [...productDataList.products];
      productsData = productsData.filter((product) => productFilters(product, byFilter));
      setFilterData(productsData);
    } else {
      setFilterData(productDataList.products);
    }
  };

  const clearFilter = () => {
    setFilterData(productDataList.products);
  };

  return (
    <div>
      <div className="filterSection">
        <div className="categoryFilters">
          <div className="filterTitle">Categories</div>
          <div className="filtersList">
            {categories.map((category) => (
              <div className="filter" key={category}>
                <div className="name" onClick={() => filter(category)}>
                  {category}
                </div>
                <div className="count">({filterData.filter(product => product.category === category).length})</div>
              </div>
            ))}
          </div>
        </div>
        <div className="brandFilters">
          <div className="filterTitle">Brands</div>
          <div className="filtersList">
            {brands.map((brand) => (
              <div className="filter" key={brand}>
                <div className="name" onClick={() => filter(brand)}>
                  {brand}
                </div>
                <div className="count">({filterData.filter(product => product.brand === brand).length})</div>
              </div>
            ))}
          </div>
        </div>
        <button onClick={clearFilter}>Clear filter</button>
      </div>
      <div className="products">
        {filterData.slice(0, productsLimit).map((product) => (
          <div className="product" key={product.id}>
            <div className="productImage">
              <img src={product.thumbnail} alt={product.title} />
            </div>
            <div className="productInfo">
              <div className="productTitle">
                <div className="productBrand">{product.brand}</div>
                <div className="productName">{product.title}</div>
              </div>
              <div className="productPrice">Price: £{product.price}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

// ReactDOM.render(<App />, document.querySelector("#root"));

有趣的是,我正在研究类似的东西,所以这是我对您的示例应用程序的改编,尝试一下,让我知道会发生什么。

© www.soinside.com 2019 - 2024. All rights reserved.