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>
</>
)
}
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.