Next.js Discord

Discord Forum

Next asking me to wrap "useSearchParams" in Suspense Boundaries but I cant seem to find where

Answered
Eulophid wasp posted this in #help-forum
Open in Discord
Eulophid waspOP
Attached is the output when i try to build, and what the specified files look like including the layout.

I wrapped the content within the files with Suspense just to be safe, but not luck on building.
Answered by joulev
Suspense must wrap the component calling the hook. So
// ❌ this does not work
function Component() {
  const sp = useSearchParams();
  return (
    <Suspense fallback={...}>
      ...
    </Suspense>
  );
}

// ✅ this works
function Component() {
  const sp = useSearchParams();
  return ...
}
function SuspensedComponent() {
  return (
    <Suspense fallback={...}>
      <Component />
    </Suspense>
  );
}
View full answer

15 Replies

Eulophid waspOP
Since u cant really see worksheets.tsx heres the code
"use client";
export default function Page() {
  const [columns, setColumns] = useState(6);
  const [worksheets, setWorksheets] = useState<WorksheetData[]>([]);
  const [selectedWorksheets, setselectedWorksheets] = useState([]);
  const [documentWriterOpen, setDocumentWriterOpen] = useState(false);

  const searchParams = useSearchParams();

  function toogleCheck(id: Key) {...}

  function DeleteWorksheets() {
    setWorksheets([...worksheets].filter((worksheet) => !worksheet.checked));
  }

  function OpenDocumentWriter() {
    setDocumentWriterOpen(true);
  }

  function CloseDocumentWriter() {
    setDocumentWriterOpen(false);
  }

  function CreateWorksheet() {
    let data: WorksheetData = {
      id: nextID++,
      checked: false,
    };

    setWorksheets([...worksheets, data]);
  }

  return (
    <>
      <Suspense>
        {searchParams.get("code") != null ? (
          <Toolbar
            OpenDocumentWriterCallback={OpenDocumentWriter}
            onDeleteWorksheetCallback={DeleteWorksheets}
          />
        ) : null}
      </Suspense>
      <Separator size="4" />
      {
        <Grid
          display="grid"
          columns={`repeat(${columns}, minmax(0, 1fr))`}
          gap="4"
          className="mt-2"
        >
          {worksheets.map((item) => {
            return (
              <AspectRatio key={item.id} ratio={1 / 1.414}>
                <Box className="w-full h-full">
                  <Card className="h-full">
                    <Checkbox onCheckedChange={() => toogleCheck(item.id)} />
                  </Card>
                </Box>
              </AspectRatio>
            );
          })}
        </Grid>
      }
      {documentWriterOpen === true ? (
        <DocumentWriter CloseDocumentWriterCallback={CloseDocumentWriter} />
      ) : null}
    </>
  );
}
@Eulophid wasp Attached is the output when i try to build, and what the specified files look like including the layout. I wrapped the content within the files with Suspense just to be safe, but not luck on building.
Suspense must wrap the component calling the hook. So
// ❌ this does not work
function Component() {
  const sp = useSearchParams();
  return (
    <Suspense fallback={...}>
      ...
    </Suspense>
  );
}

// ✅ this works
function Component() {
  const sp = useSearchParams();
  return ...
}
function SuspensedComponent() {
  return (
    <Suspense fallback={...}>
      <Component />
    </Suspense>
  );
}
Answer
@joulev Suspense must wrap the component calling the hook. So tsx // ❌ this does not work function Component() { const sp = useSearchParams(); return ( <Suspense fallback={...}> ... </Suspense> ); } tsx // ✅ this works function Component() { const sp = useSearchParams(); return ... } function SuspensedComponent() { return ( <Suspense fallback={...}> <Component /> </Suspense> ); }
Eulophid waspOP
is the fallback required cuz I just wrapped the entire component in <Suspense/> and when i built it, I got the same error. Also the only code that is using useSearchParams is Worksheet so idk why its causing the other pages to error as well
fallback is not required but if you don't specify a fallback users will just see nothingness until client-side js and router is fully loaded
@joulev in your case it's a whole page component. i guess that's related to the error. how does your worksheet page look like now?
Eulophid waspOP
 <>
      <Suspense>
        {searchParams.get("code") != null ? (
          <Toolbar
            OpenDocumentWriterCallback={OpenDocumentWriter}
            onDeleteWorksheetCallback={DeleteWorksheets}
          />
        ) : null}
        <Separator size="4" />
        {
          <Grid
            display="grid"
            columns={`repeat(${columns}, minmax(0, 1fr))`}
            gap="4"
            className="mt-2"
          >
            {worksheets.map((item) => {
              return (
                <AspectRatio key={item.id} ratio={1 / 1.414}>
                  <Box className="w-full h-full">
                    <Card className="h-full">
                      <Checkbox onCheckedChange={() => toogleCheck(item.id)} />
                    </Card>
                  </Box>
                </AspectRatio>
              );
            })}
          </Grid>
        }
        {documentWriterOpen === true ? (
          <DocumentWriter CloseDocumentWriterCallback={CloseDocumentWriter} />
        ) : null}
      </Suspense>
    </>
@joulev you are falling into the first case here. migrate your code to the second case
Eulophid waspOP
well this is what i had before which seems closer to what you were saying is the correct way
in your case would be
function PageContent() {
  const searchParams = useSearchParams();
  return searchParams.get("code") != null ?....
}
export default function Page() {
  return (
    <Suspense fallback={...}>
      <PageContent />
    </Suspense>
  );
}
@Eulophid wasp well this is what i had before which seems closer to what you were saying is the correct way
no. you are still using Suspense and searchParams in the same component, that doesn't work. you need to make a new component and wrap that one inside Suspense
Eulophid waspOP
i see ok let me try that out
@joulev no. you are still using Suspense and searchParams in the same component, that doesn't work. you need to make a new component and wrap that one inside Suspense
Eulophid waspOP
like this correct
"use client";
import { PlusIcon, TrashIcon } from "@radix-ui/react-icons";
import { Button, Flex, Skeleton } from "@radix-ui/themes";
import { useSearchParams } from "next/navigation";
import { Suspense } from "react";

function Toolbar({
  OpenDocumentWriterCallback,
  onDeleteWorksheetCallback,
}: any) {
  const searchParams = useSearchParams();

  return searchParams.get("code") != null ? (
    <Flex as="span" justify="center" align="center" gapX="2" py="2">
      <Button onClick={OpenDocumentWriterCallback}>
        Create Worksheet
        <PlusIcon />
      </Button>
      <Button onClick={onDeleteWorksheetCallback} color="red">
        Delete Worksheet
        <TrashIcon />
      </Button>
    </Flex>
  ) : null;
}

export default function ToolbarProvider({
  OpenDocumentWriterFunc,
  onDeleteWorksheetFunc,
}: any) {
  return (
    <Suspense fallback={<Skeleton />}>
      <Toolbar
        OpenDocumentWriterCallback={OpenDocumentWriterFunc}
        onDeleteWorksheetCallback={onDeleteWorksheetFunc}
      />
    </Suspense>
  );
}
@joulev yes, like that. same thing for all other places where you use `useSearchParams`
Eulophid waspOP
excellent, thanks for the assist