Skip to content

Commit

Permalink
Merge pull request #20 from ethanniser/search-race-cond
Browse files Browse the repository at this point in the history
fix search race cond
  • Loading branch information
ethanniser authored Oct 20, 2024
2 parents 3134542 + 6b1a7fd commit 1a0af64
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 60 deletions.
32 changes: 18 additions & 14 deletions src/app/api/debug/route.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { cookies } from "next/headers"
import { cookies } from "next/headers";

export async function GET(request: Request) {
// dump all the request headers, cookies, and body, infinite deep json stringify
const stringifyHeaders = JSON.stringify(Object.fromEntries(request.headers), null, 2)
const cookieStore = (await cookies()).getAll()
const stringifyCookies = JSON.stringify(cookieStore, null, 2)
// dump all the request headers, cookies, and body, infinite deep json stringify
const stringifyHeaders = JSON.stringify(
Object.fromEntries(request.headers),
null,
2,
);
const cookieStore = (await cookies()).getAll();
const stringifyCookies = JSON.stringify(cookieStore, null, 2);

console.log("headers", stringifyHeaders)
console.log("cookies", stringifyCookies)
const responseObj = {
headers: stringifyHeaders,
cookies: stringifyCookies,
}
return Response.json(responseObj)
}
console.log("headers", stringifyHeaders);
console.log("cookies", stringifyCookies);
const responseObj = {
headers: stringifyHeaders,
cookies: stringifyCookies,
};

return Response.json(responseObj);
}
101 changes: 55 additions & 46 deletions src/components/search-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import { useEffect, useState, useRef } from "react";
import { useEffect, useState, useRef, useTransition } from "react";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Search, X } from "lucide-react";
import { Search, X, Loader2 } from "lucide-react";
import Image from "next/image";
import { cn } from "@/lib/utils";
import { Product } from "../db/schema";
Expand All @@ -15,22 +15,26 @@ type SearchResult = Product & { href: string };

export function SearchDropdownComponent() {
const [searchTerm, setSearchTerm] = useState("");
const [committedSearchTerm, setCommittedSearchTerm] = useState("");
const [filteredItems, setFilteredItems] = useState<SearchResult[]>([]);
const [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
const [isPending, startTransition] = useTransition();
const router = useRouter();
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
const search = async () => {
if (searchTerm.length === 0) setFilteredItems([]);
else {
const results = await searchProducts(searchTerm);
setFilteredItems(results);
}
};

search();
if (searchTerm.length === 0) {
setFilteredItems([]);
setCommittedSearchTerm("");
} else {
startTransition(() => {
searchProducts(searchTerm).then((results) => {
setFilteredItems(results);
setCommittedSearchTerm(searchTerm);
});
});
}
}, [searchTerm]);

const params = useParams();
Expand Down Expand Up @@ -77,7 +81,11 @@ export function SearchDropdownComponent() {
onKeyDown={handleKeyDown}
className="font-sans font-medium sm:w-[300px] md:w-[375px]"
/>
<Search className="absolute right-2 top-2.5 h-4 w-4 text-muted-foreground" />
{isPending ? (
<Loader2 className="absolute right-2 top-2.5 h-4 w-4 animate-spin text-muted-foreground" />
) : (
<Search className="absolute right-2 top-2.5 h-4 w-4 text-muted-foreground" />
)}
<X
className={cn(
"absolute right-7 top-2 h-5 w-5 text-muted-foreground",
Expand All @@ -91,40 +99,41 @@ export function SearchDropdownComponent() {
}}
/>
</div>
{isOpen && filteredItems.length > 0 && (
<div className="absolute z-10 mt-1 w-full rounded-md border border-gray-200 bg-white shadow-lg">
<ScrollArea className="h-[300px]">
{filteredItems.map((item, index) => (
<Link href={item.href} key={item.slug} prefetch={true}>
<div
key={item.slug}
className={cn("flex cursor-pointer items-center p-2", {
"bg-gray-100": index === highlightedIndex,
})}
onMouseEnter={() => setHighlightedIndex(index)}
onClick={() => {
setSearchTerm(item.name);
setIsOpen(false);
inputRef.current?.blur(); // Close the keyboard on mobile
}}
>
<Image
loading="eager"
decoding="sync"
src={item.image_url ?? "/placeholder.svg"}
alt=""
className="h-10 w-10 pr-2"
height={40}
width={40}
quality={65}
/>
<span className="text-sm">{item.name}</span>
</div>
</Link>
))}
</ScrollArea>
</div>
)}
{isOpen &&
filteredItems.length > 0 &&
committedSearchTerm === searchTerm && (
<div className="absolute z-10 mt-1 w-full rounded-md border border-gray-200 bg-white shadow-lg">
<ScrollArea className="h-[300px]">
{filteredItems.map((item, index) => (
<Link href={item.href} key={item.slug} prefetch={true}>
<div
className={cn("flex cursor-pointer items-center p-2", {
"bg-gray-100": index === highlightedIndex,
})}
onMouseEnter={() => setHighlightedIndex(index)}
onClick={() => {
setSearchTerm(item.name);
setIsOpen(false);
inputRef.current?.blur();
}}
>
<Image
loading="eager"
decoding="sync"
src={item.image_url ?? "/placeholder.svg"}
alt=""
className="h-10 w-10 pr-2"
height={40}
width={40}
quality={65}
/>
<span className="text-sm">{item.name}</span>
</div>
</Link>
))}
</ScrollArea>
</div>
)}
</div>
</div>
);
Expand Down

0 comments on commit 1a0af64

Please sign in to comment.