Search code examples
reactjsnext.js

Nextjs useSearchParams is not updated correctly


I created a simple React hook to update the search params:

import { useSearchParams, usePathname, useRouter } from "next/navigation";

export const useParams = () => {
  const searchParams = useSearchParams();
  const router = useRouter();
  const pathname = usePathname();

  const setParams = (key: string, value: string) => {
    const params = new URLSearchParams(searchParams);
    params.set(key, value);
    router.replace(`${pathname}?${params}`);
  };

  return { setParams };
};

Then I use it in a client component:

export const Pagination = () => {
  const { setParams } = useParams();

  const handleChangePageSize = (size: number) => {    
    setParams("pageSize", size.toString());
  };

  const handleSetPage = (pageNumber: number) => {
    setParams("pageNumber", pageNumber.toString());
  };
// ... More code

My issue is that after handleChangePageSize is called and the pageSize param is set with a value, when then later handelSetPage is called, it's not aware of the pageSize, which was set. To be more precise, the hook lags by one change. If I remove the router.replace inside the hook (for testing), I can see that if I call handleChangePageSize twice, for example with values of 3, and then 6, then when I call handleSetPage, the params will have the value of 3, therefore it lags by one.

Why does it lag and how can I correct my hook?


Solution

  • Try making a state to track the param

    import { useState } from "react";
    import { useSearchParams, usePathname, useRouter } from "next/navigation";
    
    export const useParams = () => {
      const searchParams = useSearchParams();
      const router = useRouter();
      const pathname = usePathname();
    
      const [params, setParamsState] = useState(new URLSearchParams(searchParams));
    
      const setParams = (key: string, value: string) => {
        const updatedParams = new URLSearchParams(params);
        updatedParams.set(key, value);
        setParamsState(updatedParams);
        router.replace(`${pathname}?${updatedParams.toString()}`);
      };
    
      return { setParams };
    };
    

    page.tsx

     const { setParams } = useParams();
    
      const handleChangePageSize = (size: number) => {
        setParams('pageSize', size.toString());
      };
    
      const handleSetPage = (pageNumber: number) => {
        setParams('pageNumber', pageNumber.toString());
      };
    
    
    <button
            onClick={() => {
              handleChangePageSize(Math.floor(Math.random() * 100));
            }}
          >
            Set Page Size
          </button>
    
          <button
            onClick={() => {
              handleSetPage(Math.floor(Math.random() * 100));
            }}
          >
            Set Page
          </button>