Search code examples
javascriptreactjssortingobjectfiltering

React sort by price menu


I am making an e-commerce with two menus: filter by category and sort by price (high to low and vice versa). I've already made a filter menu, but I am not sure how to create a sort by price menu. I successfully achieved it with plain JavaScript, but I'm having some troubles understanding how to do it in React since I am still a bit new to its logics.

import React, { useEffect, useMemo, useState } from "react";
import "./VinylClocks.css";
import Navbar from "./Navbar.jsx";
import Footer from "./Footer.jsx";
import SortButton from "./SortButton.jsx";
import {data} from './data.js';
import Product from "./Product.jsx";

export default function VinylClocks() {

  const [dataList, setDataList] = useState([]);

  const [selectedCategory, setSelectedCategory] = useState();

  // Add default value on page load
  useEffect(() => {
    setDataList(data);
  }, []);

  // Function to get filtered list
  function getFilteredList() {
    // Avoid filter when selectedCategory is null
    if (!selectedCategory) {
      return dataList;
    }
    return dataList.filter((item) => item.cat === selectedCategory);
  }

  // Avoid duplicate function calls with useMemo
  var filteredList = useMemo(getFilteredList, [selectedCategory, dataList]);

  function handleCategoryChange(event) {
    setSelectedCategory(event.target.value);
  }
    return (
      <>
        <header className="header">
          <Navbar />
        </header>  
        <div className="vc-container">
          <div className="filter-sort">
            <div className="filter-container">
              <h5>Filter by Category:</h5>
              <select
              name="cat-list"
              id="cat-list"
              onChange={handleCategoryChange}
              >
                <option value="">All</option>
                <option value="movies">Movies</option>
                <option value="music">Music</option>
                <option value="sport">Sport</option>
                <option value="other">Other</option>
              </select>
            </div>

            <SortButton /> 
            
          </div> 
          <div className="products-container">
          {filteredList.map((items, id) => (
      <Product {...items} key={id} />
    ))}  
          </div>
        </div>  
          <Footer />
      </>
    );
  }

Here is the Sort menu component:

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faArrowDownWideShort } from '@fortawesome/free-solid-svg-icons';
import { useState } from 'react';
import "./SortButton.css";


export default function SortButton() {
    
const [select, setSelect] = useState("");
    return (
        <>
          <div className="sort-dropdown">
                <FontAwesomeIcon icon={faArrowDownWideShort} style={{color: "#021027",}} />
                <select value={select} 
                onChange={e=>setSelect(e.target.value)}>
                    <option>Sort by: Latest</option>
                    <option>Sort by: Price:High to Low</option>
                    <option>Sort by: Price:Low to High</option>
                </select>
            </div>
        </>
    )
}

Solution

  • To implement sorting by price in your React application, you can modify your VinylClocks component to include a state for sorting and create a function to handle the sorting logic. Here's an example:

    import React, { useEffect, useMemo, useState } from "react";
    import "./VinylClocks.css";
    import Navbar from "./Navbar.jsx";
    import Footer from "./Footer.jsx";
    import SortButton from "./SortButton.jsx";
    import { data } from "./data.js";
    import Product from "./Product.jsx";
    
    export default function VinylClocks() {
      const [dataList, setDataList] = useState([]);
      const [selectedCategory, setSelectedCategory] = useState("");
      const [sortOption, setSortOption] = useState("latest");
    
      // Add default value on page load
      useEffect(() => {
        setDataList(data);
      }, []);
    
      // Function to get filtered and sorted list
      function getFilteredAndSortedList() {
        let filteredList = dataList;
        // Filter by category
        if (selectedCategory) {
          filteredList = dataList.filter((item) => item.cat === selectedCategory);
        }
    
        // Sort by price
        switch (sortOption) {
          case "highToLow":
            filteredList.sort((a, b) => b.price - a.price);
            break;
          case "lowToHigh":
            filteredList.sort((a, b) => a.price - b.price);
            break;
          // Add other cases for additional sort options if needed
          default:
            // Latest or default sorting
            // You can add more cases or modify this based on your requirements
            break;
        }
    
        return filteredList;
      }
    
      // Avoid duplicate function calls with useMemo
      var filteredAndSortedList = useMemo(getFilteredAndSortedList, [
        selectedCategory,
        sortOption,
        dataList,
      ]);
    
      function handleCategoryChange(event) {
        setSelectedCategory(event.target.value);
      }
    
      function handleSortChange(option) {
        setSortOption(option);
      }
    
      return (
        <>
          <header className="header">
            <Navbar />
          </header>
          <div className="vc-container">
            <div className="filter-sort">
              <div className="filter-container">
                <h5>Filter by Category:</h5>
                <select
                  name="cat-list"
                  id="cat-list"
                  onChange={handleCategoryChange}
                  value={selectedCategory}
                >
                  <option value="">All</option>
                  <option value="movies">Movies</option>
                  <option value="music">Music</option>
                  <option value="sport">Sport</option>
                  <option value="other">Other</option>
                </select>
              </div>
    
              <SortButton onSortChange={handleSortChange} />
    
            </div>
            <div className="products-container">
              {filteredAndSortedList.map((item, id) => (
                <Product {...item} key={id} />
              ))}
            </div>
          </div>
          <Footer />
        </>
      );
    }
    

    And modify your SortButton component to call the onSortChange prop when the user selects a different sorting option:

    import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
    import { faArrowDownWideShort } from "@fortawesome/free-solid-svg-icons";
    import { useState } from "react";
    import "./SortButton.css";
    
    export default function SortButton({ onSortChange }) {
      const [select, setSelect] = useState("");
    
      function handleSortChange(e) {
        setSelect(e.target.value);
        onSortChange(e.target.value);
      }
    
      return (
        <>
          <div className="sort-dropdown">
            <FontAwesomeIcon
              icon={faArrowDownWideShort}
              style={{ color: "#021027" }}
            />
            <select value={select} onChange={handleSortChange}>
              <option value="latest">Sort by: Latest</option>
              <option value="highToLow">Sort by: Price: High to Low</option>
              <option value="lowToHigh">Sort by: Price: Low to High</option>
            </select>
          </div>
        </>
      );
    }
    

    With these changes, the VinylClocks component will now handle both filtering by category and sorting by price. The SortButton component sends the selected sorting option back to the parent component via the onSortChange prop, triggering the sorting logic in the parent component.