Created
June 10, 2025 19:00
-
-
Save bkanhu/fba1a4be5457c763333639a4a20ee710 to your computer and use it in GitHub Desktop.
NextJS Filter component with SearchParams and Custom Hooks.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { useRouter, useSearchParams } from "next/navigation"; | |
import React from "react"; | |
const FilterPanel = () => { | |
const router = useRouter(); | |
const searchParams = useSearchParams(); | |
const handleFilterChange = (e) => { | |
const params = new URLSearchParams(searchParams.toString()); | |
const { name, value } = e.target; | |
if (value) { | |
params.set(name, value); | |
} else { | |
params.delete(name); | |
} | |
// Reset pagination when filter changes | |
params.delete("page"); | |
router.replace(`?${params.toString()}`); | |
}; | |
return ( | |
<div> | |
<div> | |
<select | |
name="orderStatus" | |
id="orderStatus" | |
value={searchParams.get("orderStatus") || ""} | |
onChange={handleFilterChange} | |
> | |
<option value="">Choose a status</option> | |
<option value="initiated">Initiated</option> | |
<option value="in progress">In Progress</option> | |
<option value="shipped">Shipped</option> | |
<option value="delivered">Delivered</option> | |
<option value="completed">Completed</option> | |
</select> | |
</div> | |
<div> | |
<label htmlFor="startDate">Start Date</label> | |
<input | |
type="date" | |
name="startDate" | |
id="startDate" | |
value={searchParams.get("startDate") || ""} | |
onChange={handleFilterChange} | |
/> | |
</div> | |
<div> | |
<label htmlFor="endDate">End Date</label> | |
<input | |
type="date" | |
name="endDate" | |
id="endDate" | |
value={searchParams.get("endDate") || ""} | |
onChange={handleFilterChange} | |
/> | |
</div> | |
</div> | |
); | |
}; | |
export default FilterPanel; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import FilterPanel from "./FilterPanel"; | |
import ProductSection from "./ProductSection"; | |
const FilterPage = () => { | |
return ( | |
<div> | |
<h1>FilterPage</h1> | |
<FilterPanel /> | |
<ProductSection /> | |
</div> | |
); | |
}; | |
export default FilterPage; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { | |
Pagination, | |
PaginationContent, | |
PaginationItem, | |
PaginationNext, | |
PaginationPrevious, | |
} from "@/components/ui/pagination"; | |
import { useRouter, useSearchParams } from "next/navigation"; | |
const ProductSection = () => { | |
return ( | |
<div> | |
ProductSection | |
<OrderPagination totalPages={10} /> | |
</div> | |
); | |
}; | |
const OrderPagination = ({ totalPages }) => { | |
const router = useRouter(); | |
const searchParams = useSearchParams(); | |
const currentPage = parseInt(searchParams.get("page") || "1", 10); | |
const goToPage = (page) => { | |
const params = new URLSearchParams(searchParams.toString()); | |
params.set("page", String(page)); | |
router.replace(`?${params.toString()}`); | |
}; | |
return ( | |
<Pagination> | |
<PaginationContent> | |
<PaginationItem> | |
<PaginationPrevious | |
onClick={() => { | |
if (currentPage > 1) goToPage(currentPage - 1); | |
}} | |
className={ | |
currentPage === 1 ? "pointer-events-none opacity-50" : "" | |
} | |
/> | |
</PaginationItem> | |
<PaginationItem> | |
<span className="rounded border border-input bg-muted px-3 py-1 text-sm"> | |
Page {currentPage} of {totalPages} | |
</span> | |
</PaginationItem> | |
<PaginationItem> | |
<PaginationNext | |
onClick={() => { | |
if (currentPage < totalPages) goToPage(currentPage + 1); | |
}} | |
className={ | |
currentPage === totalPages ? "pointer-events-none opacity-50" : "" | |
} | |
/> | |
</PaginationItem> | |
</PaginationContent> | |
</Pagination> | |
); | |
}; | |
export default ProductSection; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { useSearchParams } from "next/navigation"; | |
// Custom hook to get orderStatus, startDate, endDate, and page from URL. | |
const useOrderQueryParams = () => { | |
const searchParams = useSearchParams(); | |
const orderStatus = searchParams.get("orderStatus") || ""; | |
const startDate = searchParams.get("startDate") || ""; | |
const endDate = searchParams.get("endDate") || ""; | |
const page = parseInt(searchParams.get("page") || "1", 10); | |
return { orderStatus, startDate, endDate, page }; | |
}; | |
export default useOrderQueryParams; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { useEffect, useState } from "react"; | |
import { useOrderQueryParams } from "./useOrderQueryParams"; | |
export const useOrders = () => { | |
const { orderStatus, startDate, endDate, page } = useOrderQueryParams(); | |
const [data, setData] = useState(null); | |
const [isLoading, setIsLoading] = useState(false); | |
const [error, setError] = useState(null); | |
useEffect(() => { | |
const controller = new AbortController(); | |
const fetchOrders = async () => { | |
setIsLoading(true); | |
setError(null); | |
try { | |
const params = new URLSearchParams(); | |
if (orderStatus) params.set("orderStatus", orderStatus); | |
if (startDate) params.set("startDate", startDate); | |
if (endDate) params.set("endDate", endDate); | |
params.set("page", page.toString()); | |
const res = await fetch(`/api/orders?${params.toString()}`, { | |
signal: controller.signal, | |
}); | |
if (!res.ok) throw new Error("Failed to fetch orders"); | |
const json = await res.json(); | |
setData(json); | |
} catch (err) { | |
if (err.name !== "AbortError") { | |
setError(err.message || "Unknown error"); | |
} | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
fetchOrders(); | |
return () => controller.abort(); | |
}, [orderStatus, startDate, endDate, page]); | |
return { data, isLoading, error }; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment