I have in issue with filter function in my react ecommerce app

React filter function is working

When I click on the filter option like "Denim", "shirt" etc it should filter the products and show only those items only. I used the handleTag function to filter the array of products and then update the setfilterProducts but it is not working.

Below my main file Home.jsx

function Home() {
    const [products, setProducts] = useState([]);
    const [filterProducts, setFileterProducts] = useState([]);
    const [sortValue, setSortValue] = useState('');
    const [tag, setTag] = useState('');
    // const [error, setError] = useState('');

    const loadAllProducts = () => {

    /*var myArray = ['a', 1, 'a', 2, '1'];

    let unique = [...new Set(myArray)];

    console.log(unique); // unique is ['a', 1, 2, '1']*/
    let ar = data.map(item => item.tag);
    let uniqueEle = [...new Set(ar)];
    // console.log(uniqueEle);
    setProducts(data);
    setFileterProducts(data);
}

useEffect(() => {
    loadAllProducts();
    handleSort();
    handleTag();
}, [filterProducts, sortValue, tag])

const handleSort = (value) => {
    setSortValue(value);
    listProducts();
}

function listProducts() {
    
    if(sortValue !== '') {
        // console.log("SORT: "+sortValue)
        products.sort((a, b) => sortValue === 'lowest' ? (parseInt(a.price) > parseInt(b.price) ? 1:-1) : (parseInt(a.price) < parseInt(b.price) ? 1:-1))
    } else {
        // console.log("INSIDE THE ELSE PART");
        products.sort((a, b) => (a.id > b.id ? 1 : -1))
    }
    return {setFileterProducts: products}
}

const handleTag = (value) => {
    // console.log(value);
    setTag(value);
    listTagProduct();
}

function listTagProduct() {
    if(tag !== '') {
        if(tag === 'Denim') {
            console.log(tag);
            let filterAr = tagFilter(tag);
            console.log("FilterArr "+filterAr);
            return {setFileterProducts: filterAr}
        } else if(tag === "T-shirt") {
            let filterAr = tagFilter(tag);
            return {setFileterProducts: filterAr}
        } else if(tag === "shirt") {
            let filterAr = tagFilter(tag);
            return {setFileterProducts: filterAr}
        } else if(tag === "jacket") {
            let filterAr = tagFilter(tag);
            return {setFileterProducts: filterAr}
        }
    }
    return {setFileterProducts: products}
}

// utility function
function tagFilter(tag) {
    let ar = filterProducts.filter(item => item.tag === "Denim");
    // console.log(ar);
    return ar;
}

return (
    <div className =  "container-fluid">
        <h2>All Products: {filterProducts.length}</h2>
        <Filter 
            product = {filterProducts} 
            handleSort = {handleSort} 
            sort = {sortValue}
            handleTag = {handleTag}
            tag = {tag}
        />
        <hr />
        <div className="row custom-card">
                {filterProducts.map((product, idx) => {
                    console.log(product);
                    return(
                        
                   

     <div key = {product.id} className="col-md-3 mb-2">
                                <Card products = {product}/>
                            </div>
                        )
                    })}
            </div>
        </div>
    )
}

    export default Home

This uses to display the filter options Filter.jsx

function Filter(props) {
    const handleChange = (event) => {
        // console.log(event.target.value);
        return props.handleSort(event.target.value);
    }

const handleTagChange = (event) => {
    // console.log(event.target.value);
    return props.handleTag(event.target.value);
}
let tagNames = ["T-shirt", "shirt", "Denim", "jacket"];
return (
    <div className="container-fluid">
        <div className="row">
            <div className="col-12 col-md-9">
                <p className = " mr-2">Filters:<span className="mr-4"></span>
                    {tagNames.map((item, idx) => (
                        <span key = {idx} >
                            <button 
                                type="button" 
                                class="btn btn-outline-secondary mr-2 custom-filter"
                                value= {item}
                                onClick = {handleTagChange}
                            >{item}</button>
                        </span>
                    ))}
                </p>
                </div>
            <div className="col-12 col-md-3">
            <select className="form-control" value = {props.sort} onChange = {handleChange}>
                {/* {console.log("CHECKING: "+props.sort)} */}
                <option value="">Sort by</option>
                <option value="lowest">lowest to highest</option>
                <option value="highest">highest to lowest</option>
            </select>
            </div>
        </div>
    </div>
)

}

    export default Filter

codesandbox link

filter is not working(when I click on the filter option like Denim it should only the Denim product)

20 thoughts on “I have in issue with filter function in my react ecommerce app”

  1. Your Filter.jsx looks alright and should work fine. I only made a few simple changes like moving the onClick event handlers inline, and added the selected button some styles to distinguish its state.

    Mutating the state directly

    On the other hand, your Home.jsx looks overcomplicated, so I needed to clean things up. One of the biggest mistakes was mutating the states directly. It can lead to really odd bugs. You probably didn’t even noticed that you changed the state, but products.sort for example mutates the original products array.

    You should clone the array first (you can use .slice() or the spread operator), sort the clone, and update the state with this new sorted array.

    Weird return statements

    Another problem is returning objects like {setFileterProducts: products}, or {setFileterProducts: filterAr}. I’m not sure what you wanted to do, maybe it meant to be setFileterProducts(filterAr)?

    The useEffect hooks

    The next problem is the usage of the useEffect hook. You tried to loadAllProducts every time a filter tag, a sort by value, or the filtered products change which is absolutely unnecessary.

    You could instead use two useEffects; one to load the data from the API and set the products and filteredProducts states, and another one to update filteredProducts every time the tag or sort value changes. You don’t need anything else because whenever the filteredProducts array changes, React re-renders the component.

    Some more simplification

    I changed a few variable names and I tried to remove every unnecessary lines from Home.jsx; only the states, the two useEffect hooks, and the return statement stayed. Filter and sort is handled by the hook, there is no need to use additional functions for these.

    I also simplified the sort functions. If you want to sort numbers, you just need to subtract them (and as subtraction changes the price’s type to a number, you don’t even need to use parseInt anymore).

    Home.jsx

    import React, { useEffect, useState } from "react";
    
    import data from "../../../data.json";
    import Card from "../card/Card.jsx";
    import Filter from "../filter/Filter.jsx";
    import "./Home.css";
    
    function Home() {
      const [products, setProducts] = useState([]);
      const [filteredProducts, setFilteredProducts] = useState([]);
      const [sortBy, setSortBy] = useState("");
      const [selectedTag, setSelectedTag] = useState("");
    
      useEffect(() => {
        setProducts(data);
        setFilteredProducts(data);
      }, []);
    
      useEffect(() => {
        const filtered = selectedTag
          ? products.filter((item) => item.tag === selectedTag)
          : products;
    
        setFilteredProducts(
          sortBy
            ? [...filtered].sort((a, b) =>
                sortBy === "lowest" ? a.price - b.price : b.price - a.price
              )
            : [...filtered].sort((a, b) => (a.id > b.id ? 1 : -1))
        );
      }, [selectedTag, sortBy, products]);
    
      return (
        <div className="container-fluid">
          <h2>Products: {filteredProducts.length}</h2>
          <Filter
            handleSort={setSortBy}
            handleTagChange={setSelectedTag}
            selectedTag={selectedTag}
            sortBy={sortBy}
          />
          <hr />
          <div className="row custom-card">
            {filteredProducts.map((product) => (
              <div key={product.id} className="col-md-3 mb-2">
                <Card products={product} />
              </div>
            ))}
          </div>
        </div>
      );
    }
    
    export default Home;
    

    Filter.jsx:

    import React from "react";
    import "./Filter.css";
    
    function Filter({ handleSort, handleTagChange, selectedTag, sortBy }) {
      let tagNames = ["T-shirt", "shirt", "Denim", "jacket"];
    
      return (
        <div className="container-fluid">
          <div className="row">
            <div className="col-12 col-md-9">
              <p className=" mr-2">
                Filters: <span className="mr-4"></span>
                {tagNames.map((tag, idx) => (
                  <button
                    key={idx}
                    type="button"
                    className={`btn mr-2 custom-filter ${
                      selectedTag === tag
                        ? "btn-secondary"
                        : "btn-outline-secondary"
                    }`}
                    onClick={(e) => handleTagChange(e.target.value)}
                    value={tag}
                  >
                    {tag}
                  </button>
                ))}
              </p>
            </div>
            <div className="col-12 col-md-3">
              <select
                className="form-control"
                onChange={(e) => handleSort(e.target.value)}
                value={sortBy}
              >
                <option value="">Sort by</option>
                <option value="lowest">Lowest to highest</option>
                <option value="highest">Highest to lowest</option>
              </select>
            </div>
          </div>
        </div>
      );
    }
    
    export default Filter;
    

    CodeSandbox link

    Reply

Leave a Comment