最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - useSearchParams() should be wrapped in a suspense boundary at page "findsearchResults" - Stack Ov

programmeradmin0浏览0评论

Ive tried wrapping my code in Suspense Tags but Im still getting the following error while building production build of my nextjs app.

 ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/find/searchResults". Read more: 
    at a (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:6747)
    at f (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:23270)
    at h (/home/yash/nextjs-hireme/hireme/.next/server/app/find/searchResults/page.js:1:3734)
    at nO (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:45959)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47734)
    at nL (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:65533)
    at nN (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:63164)
    at n$ (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:46311)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47780)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:62515)
Error occurred prerendering page "/find/searchResults". Read more: 
Export encountered an error on /find/searchResults/page: /find/searchResults, exiting the build.
 ⨯ Static worker exited with code: 1 and signal: null

Ive also tried reinstalling the dependencies but nothing works. This page displays the search results fetched by the search-services component. Here's my folder structure:

app/
├── components/
|      ├── search-services.jsx 
|
├── find/
│     ├── searchResults/
│     |         ├── page.jsx
│     ├── page.jsx

Here's my code:

"use client";

import React, { useEffect, useState, Suspense } from "react";
import { useSearchParams } from "next/navigation";
import searchServices from "../../components/search-services";
import Link from "next/link";
import ServiceDetails from "../../components/service-details";
import { auth, db } from "../../firebase/config";
import { doc, getDoc } from "firebase/firestore";
import calculateDistance from "../../components/calculate-distance";

const SearchResults = () => {
  const searchParams = useSearchParams();
  const searchTerm = searchParams.get("query").trim() || "";
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(true);
  const [selectedService, setSelectedService] = useState(null);
  const [userLocation, setUserLocation] = useState(null);

  const handleServiceClick = (service) => setSelectedService(service);
  const handleCloseDetails = () => setSelectedService(null);

  // Fetch user location from Firestore
  useEffect(() => {
    const fetchUserLocation = async () => {
      auth.onAuthStateChanged(async (user) => {
        if (user) {
          const docRef = doc(db, "Users", user.uid);
          const docSnap = await getDoc(docRef);
          if (docSnap.exists()) {
            const userData = docSnap.data();
            if (userData.location) {
              setUserLocation(userData.location); // Assume location is { latitude, longitude }
            }
          }
        } else {
          console.log("User is not logged in");
        }
      });
    };

    fetchUserLocation();
  }, []);

  // Fetch search results
  useEffect(() => {

    const fetchResults = async () => {
      if (!searchTerm) return;
      setLoading(true);
      try {
        const services = await searchServices(searchTerm);
        setResults(services);
      } catch (error) {
        console.error("Error fetching search results:", error);
      }
      setLoading(false);
    };

    fetchResults();

  }, [searchTerm]);

  return (
    <Suspense>
      <div className="min-h-screen bg-gray-50 px-4 py-6">
        <h1 className="text-2xl font-semibold mb-4">Search Results for "{searchTerm}"</h1>
        <Link href="/find" className="text-teal-600 mt-4 inline-block">
          ← Back to Search
        </Link>
    
        {loading ? (
          <p>Loading...</p>
        ) : results.length === 0 ? (
          <p className="text-gray-500">No services found.</p>
        ) : (
          <div className="grid grid-cols-1 gap-4">
          {results.map((service) => {
            const distance = userLocation && service.location 
              ? calculateDistance(userLocation, service.location) 
              : null;
            return (
              <div key={service.id} className="relative p-4 border rounded-lg cursor-pointer hover:bg-gray-50 transition-colors shadow-sm" onClick={(e) => {
                e.stopPropagation();
                handleServiceClick(service);
              }}>
                <div className="flex justify-between items-center">
                  <h2 className="text-lg font-semibold text-gray-800">{service.title}</h2>
                </div>
                <p className="text-gray-600 mt-2 line-clamp-2">{service.description}</p>
                <div className="mt-2">
                  <div className="flex justify-between">
                    <div>
                      <p className="text-sm text-gray-500">Provider: {service.providerName}</p>
                      <p className="text-sm text-gray-500">Phone: {service.providerPhone}</p>
                      <p className="text-sm text-gray-500">Email: {service.providerEmail}</p>
                    </div>
                    {distance !== null && (
                      <div className="text-right">
                        <span className="text-md font-bold text-blue-700">
                          {distance.toFixed(2)} km
                        </span>
                      </div>
                    )}
                  </div>
                </div>
                <div className="mt-4 flex justify-between items-center">
                  <p className="text-teal-600 font-bold mb-2">
                    ₹{parseFloat(service.price.min).toFixed(2)} - ₹{parseFloat(service.price.max).toFixed(2)}
                  </p>
                  <span className="text-sm text-gray-500">{new Date(service.timestamp).toLocaleDateString('en-GB')}</span>
                </div>
              </div>
            );
          })}
        </div>
        )}
  
        {/* Display selected service details */}
        {selectedService && (
          <ServiceDetails
            key={selectedService.userId}
            userId={selectedService.userId}
            service={selectedService}
            onClose={handleCloseDetails}
          />
        )}
      </div>
    </Suspense>
  );
};

export default SearchResults;

I am running Next.js 15.1.6

Ive tried wrapping my code in Suspense Tags but Im still getting the following error while building production build of my nextjs app.

 ⨯ useSearchParams() should be wrapped in a suspense boundary at page "/find/searchResults". Read more: https://nextjs./docs/messages/missing-suspense-with-csr-bailout
    at a (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:6747)
    at f (/home/yash/nextjs-hireme/hireme/.next/server/chunks/244.js:1:23270)
    at h (/home/yash/nextjs-hireme/hireme/.next/server/app/find/searchResults/page.js:1:3734)
    at nO (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:45959)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47734)
    at nL (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:65533)
    at nN (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:63164)
    at n$ (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:46311)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:47780)
    at nI (/home/yash/nextjs-hireme/hireme/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:20:62515)
Error occurred prerendering page "/find/searchResults". Read more: https://nextjs./docs/messages/prerender-error
Export encountered an error on /find/searchResults/page: /find/searchResults, exiting the build.
 ⨯ Static worker exited with code: 1 and signal: null

Ive also tried reinstalling the dependencies but nothing works. This page displays the search results fetched by the search-services component. Here's my folder structure:

app/
├── components/
|      ├── search-services.jsx 
|
├── find/
│     ├── searchResults/
│     |         ├── page.jsx
│     ├── page.jsx

Here's my code:

"use client";

import React, { useEffect, useState, Suspense } from "react";
import { useSearchParams } from "next/navigation";
import searchServices from "../../components/search-services";
import Link from "next/link";
import ServiceDetails from "../../components/service-details";
import { auth, db } from "../../firebase/config";
import { doc, getDoc } from "firebase/firestore";
import calculateDistance from "../../components/calculate-distance";

const SearchResults = () => {
  const searchParams = useSearchParams();
  const searchTerm = searchParams.get("query").trim() || "";
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(true);
  const [selectedService, setSelectedService] = useState(null);
  const [userLocation, setUserLocation] = useState(null);

  const handleServiceClick = (service) => setSelectedService(service);
  const handleCloseDetails = () => setSelectedService(null);

  // Fetch user location from Firestore
  useEffect(() => {
    const fetchUserLocation = async () => {
      auth.onAuthStateChanged(async (user) => {
        if (user) {
          const docRef = doc(db, "Users", user.uid);
          const docSnap = await getDoc(docRef);
          if (docSnap.exists()) {
            const userData = docSnap.data();
            if (userData.location) {
              setUserLocation(userData.location); // Assume location is { latitude, longitude }
            }
          }
        } else {
          console.log("User is not logged in");
        }
      });
    };

    fetchUserLocation();
  }, []);

  // Fetch search results
  useEffect(() => {

    const fetchResults = async () => {
      if (!searchTerm) return;
      setLoading(true);
      try {
        const services = await searchServices(searchTerm);
        setResults(services);
      } catch (error) {
        console.error("Error fetching search results:", error);
      }
      setLoading(false);
    };

    fetchResults();

  }, [searchTerm]);

  return (
    <Suspense>
      <div className="min-h-screen bg-gray-50 px-4 py-6">
        <h1 className="text-2xl font-semibold mb-4">Search Results for "{searchTerm}"</h1>
        <Link href="/find" className="text-teal-600 mt-4 inline-block">
          ← Back to Search
        </Link>
    
        {loading ? (
          <p>Loading...</p>
        ) : results.length === 0 ? (
          <p className="text-gray-500">No services found.</p>
        ) : (
          <div className="grid grid-cols-1 gap-4">
          {results.map((service) => {
            const distance = userLocation && service.location 
              ? calculateDistance(userLocation, service.location) 
              : null;
            return (
              <div key={service.id} className="relative p-4 border rounded-lg cursor-pointer hover:bg-gray-50 transition-colors shadow-sm" onClick={(e) => {
                e.stopPropagation();
                handleServiceClick(service);
              }}>
                <div className="flex justify-between items-center">
                  <h2 className="text-lg font-semibold text-gray-800">{service.title}</h2>
                </div>
                <p className="text-gray-600 mt-2 line-clamp-2">{service.description}</p>
                <div className="mt-2">
                  <div className="flex justify-between">
                    <div>
                      <p className="text-sm text-gray-500">Provider: {service.providerName}</p>
                      <p className="text-sm text-gray-500">Phone: {service.providerPhone}</p>
                      <p className="text-sm text-gray-500">Email: {service.providerEmail}</p>
                    </div>
                    {distance !== null && (
                      <div className="text-right">
                        <span className="text-md font-bold text-blue-700">
                          {distance.toFixed(2)} km
                        </span>
                      </div>
                    )}
                  </div>
                </div>
                <div className="mt-4 flex justify-between items-center">
                  <p className="text-teal-600 font-bold mb-2">
                    ₹{parseFloat(service.price.min).toFixed(2)} - ₹{parseFloat(service.price.max).toFixed(2)}
                  </p>
                  <span className="text-sm text-gray-500">{new Date(service.timestamp).toLocaleDateString('en-GB')}</span>
                </div>
              </div>
            );
          })}
        </div>
        )}
  
        {/* Display selected service details */}
        {selectedService && (
          <ServiceDetails
            key={selectedService.userId}
            userId={selectedService.userId}
            service={selectedService}
            onClose={handleCloseDetails}
          />
        )}
      </div>
    </Suspense>
  );
};

export default SearchResults;

I am running Next.js 15.1.6

Share Improve this question asked Feb 1 at 18:09 Yash KumarYash Kumar 217 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 3

I would not recommend rendering the whole page client side, but if you want to keep it this way you can do this:

app/
├── find/
│     ├── searchResults/
│     │         ├── layout.tsx
│     |         ├── page.jsx

in the layout.tsx you can use suspense component and wrap children prop inside:

import { Suspense } from "react";
export default function SRLayout({ children }: { children: React.ReactNode }) {
   return (
      <Suspense>
         {children}
      </Suspense>
   )
}

This approach is not ideal since it beats the whole point of partial prerendering and server side rendering.

Solved it by by calling the SearchResults component inside the Page component and wrapping it in a Suspense block.

import { Suspense } from "react";

const searchResults = () => {
    // code
}

const Page = () => {
    return (
        <Suspense>
            <searchResults />
        </Suspense>
    )
}

export default Page

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论