fetchProducts should only be called once! whenever the searchParams change it should just use the existing fetchProducts response... same for subcategories... I'm using nextjs app router.
import React from 'react';
import ProductList from '@/components/ProductList';
import AsyncNavBar from '@/components/AsyncNavBar';
import { cache } from 'react';
import Link from "next/link";

const ITEMS_PER_PAGE = 20;
const REVALIDATE_TIME = 14400; // 4 hours

// Helper function to construct query strings
const constructQueryParams = (params) => {
    const query = new URLSearchParams();
    for (const [key, value] of Object.entries(params)) {
        if (
            value !== '' &&
            value !== undefined &&
            value !== null &&
            !Number.isNaN(value)
        ) {
            query.set(key, value);
        }
    }
    return query.toString();
};

// Fetch products with API filtering and caching
const fetchProducts = cache(async (params) => {
    const queryString = constructQueryParams(params);
    const url = `https://api.naayiq.com/products?${queryString}`;

    const response = await fetch(url, {
        headers: { 'Content-Type': 'application/json' },
        cache: 'force-cache',
        next: { revalidate: REVALIDATE_TIME },
    });

    if (!response.ok) {
        throw new Error('Failed to fetch products');
    }

    return response.json();
});

// Memoize the getFilteredProducts function to cache results based on parameters
const getFilteredProducts = cache(async (params) => {
    const {
        page = 1,
        query = '',
        c = '',
        sc = '',
        b = '',
        minPriceA = 0,
        maxPriceA = Infinity,
        availability = 'all',
        sortBy = '',
    } = params;

    // Prepare API parameters
    const apiParams = {
        page,
        query,
        category: c,
        subCategory: sc,
        brand: b,
        minPrice: minPriceA,
        maxPrice: maxPriceA === Infinity ? undefined : maxPriceA,
        availability,
        sortBy,
        limit: ITEMS_PER_PAGE,
    };

    // Fetch products using API filtering and caching
    const productsData = await fetchProducts(apiParams);

    const products = productsData.products || [];

    // Optimize min and max price calculation
    let minPrice = productsData.minPrice ?? Infinity;
    let maxPrice = productsData.maxPrice ?? -Infinity;

    // If the API doesn't provide minPrice and maxPrice, calculate them
    if (minPrice === Infinity || maxPrice === -Infinity) {
        products.forEach((product) => {
            product.sizes.forEach((size) => {
                const price = parseFloat(size.price);
                if (price < minPrice) minPrice = price;
                if (price > maxPrice) maxPrice = price;
            });
        });
    }

    const totalProducts = productsData.total ?? products.length;
    const hasMore = page * ITEMS_PER_PAGE < totalProducts;

    return {
        products: products.slice(0, page * ITEMS_PER_PAGE),
        totalProducts,
        hasMore,
        minPrice,
        maxPrice,
    };
});

// The main ProductsPage component
const ProductsPage = async ({ searchParams }) => {
    const {
        page = '1',
        query = '',
        c = '',
        sc = '',
        b = '',
        minPrice = '0',
        maxPrice = '',
        availability = 'all',
        sortBy = '',
        title = '',
    } = searchParams;

    // Parse numeric parameters
    const pageNum = parseInt(page, 10) || 1;
    const minPriceNum = parseFloat(minPrice) || 0;
    const maxPriceNum = maxPrice ? parseFloat(maxPrice) : Infinity;

    try {
        const {
            products,
            totalProducts,
            hasMore,
            minPrice: minPriceResult,
            maxPrice: maxPriceResult,
        } = await getFilteredProducts({
            page: pageNum,
            query,
            c,
            sc,
            b,
            minPriceA: minPriceNum,
            maxPriceA: maxPriceNum,
            availability,
            sortBy,
        });

        return (
            <>
                <AsyncNavBar />
                <ProductList
                    initialProducts={products}
                    totalProducts={totalProducts}
                    hasMore={hasMore}
                    initialPage={pageNum}
                    initialQuery={query}
                    initialC={c}
                    initialSc={sc}
                    initialB={b}
                    minPrice={minPriceResult}
                    maxPrice={maxPriceResult}
                    title={sortBy || title || 'All Products'}
                />
            </>
        );
    } catch (error) {
        // Enhanced error handling
        console.error('Error fetching products:', error);
        return (
            <div className="font-serif flex justify-center items-center">
                Oops! Something went wrong while loading products. Please try again later.
                <Link href={"/"}>Go back home</Link>
            </div>
        );
    }
};

export default ProductsPage;