I have a very basic project structure with just a root route. In my page.tsx
, which is a server component, I fetch and show a list of countries from REST countries API endpoint.
I also have a client component search-bar.tsx
which has a search input to search and filter for countries, the results of which will be shown inside of page.tsx
My problem is once I search for a country for which the API returns a 404 and my error.tsx
error boundary component UI is rightly triggered, I am not able to re-render the server component inside page.tsx
back from error.tsx
. I have used all variations like router.push("/")
, revalidatePath()
and router.refresh()
Code files below:
pages.tsx
import Navbar from "./navbar";
import SearchBar from "./search-bar";
import Select from "./select";
import Countries from "./countries";
const getCountriesData = async () => {
const res = await fetch("https://restcountries.com/v3.1/all", {
cache: "no-store"
});
if (!res.ok) {
throw new Error("Failed to fetch all data");
}
return res.json();
};
const getCountryByName = async (countryName: string) => {
const res = await fetch(`https://restcountries.com/v3.1/name/${countryName}`);
if (!res.ok) {
throw new Error("Failed to fetch country data");
}
return res.json();
}
export default async function Home({params, searchParams}: {params: Params, searchParams: SearchParams}) {
const countries: Array<any> = searchParams.search ? await getCountryByName(searchParams.search as string) : await getCountriesData()
return (
<main className="flex flex-col gap-2 overflow-x-hidden">
<header>
<Navbar />
</header>
<div className="flex justify-between p-2 md:p-4">
<SearchBar />
<Select />
</div>
<div className="flex flex-wrap justify-between gap-4 p-2 md:p-4">
<Countries countries={countries} />
</div>
</main>
);
}
search-bar.tsx
"use client";
import { useRouter } from "next/navigation";
import { ChangeEvent, useEffect, useMemo, useState } from "react";
const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState("");
const router = useRouter();
const debounce = <F extends (...args: Parameters<F>) => ReturnType<F>> (fn: F, delay: number) => {
let timer: ReturnType<typeof setTimeout>
return (...args: Parameters<F>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
}
}
const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value);
router.push(`/?search=${e.target.value}`)
};
const debouncedSearch = debounce(handleSearch, 500)
return (
<div className="flex gap-1 items-center">
<img
src="/search-magnifying-glass-svgrepo-com.svg"
alt="search icon"
width={20}
height={20}
/>
<input
type="text"
name="search-country"
placeholder="Search for a country..."
className="url"
onChange={(e) => debouncedSearch(e)}
/>
</div>
);
};
export default SearchBar;
error.tsx
"use client";
import { revalidatePath } from "next/cache";
import Link from "next/link";
import { redirect, useRouter } from "next/navigation";
import { useEffect } from "react";
const Error = ({error, reset}: {error: Error, reset: () => void}) => {
console.log(error);
const router = useRouter();
reset = () => {
router.push("/");
router.refresh();
}
useEffect(() => {
console.error(error);
}, [error])
return (
<div>
<h2 className="text-red-400">Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>Here
</button>
{/* <Link href="/">Click here to go back</Link> */}
</div>
)
}
export default Error;
Please note I am able to reset the path back to "/" aka root route from error.tsx
and an API call is fired to get all countries but the component or UI in page.tsx
is not rendered and I keep seeing the error boundary UI
Any help is appreciated!