Search code examples
javascriptreactjsreact-router-dom

React Router Dom and object in in search parameters


I am using React Router Dom v6. I would like to store some object search parameters in the URL. Right now, I am basically doing something like this:

  const [searchParams, setSearchParams] = useSearchParams();
  const allSearchParams = useMemo(() => {
    const params: Record<string, string> = {};
    for (const [key, value] of searchParams.entries()) {
      if (value.startsWith('{') || value.startsWith('[')) {
        try {
          params[key] = JSON.parse(value);
        } catch {
          params[key] = value;
        }
      } else {
        params[key] = value;
      }
    }
    return params;
  }, [searchParams]);

And when writing in the URL, I do:

      const newF: Record<string, string> = { ...nextFilter };
      Object.keys(newF).forEach((key) => {
        if (typeof newF[key] === 'object' && newF[key]) {
          newF[key] = JSON.stringify(newF[key]);
        }
      });
      setSearchParams(createSearchParams(newF), {
        replace: replaceSearch,
      });

But this feels pretty hacky. Is there a proper way to store objects in the URL properly and safely? For example:

const filters = {
 name: "user1",
 region: {
   city: "Sydney",
   country: "Australia"
 }
}

Solution

  • You can simplify your encoding and decoding process. You can use the below functions; you can add them in their own file and import them:

    import {createSearchParams} from "react-router-dom"
    
    export const encodeSearchParams = (params) => createSearchParams(params);
    
    export const decodeSearchParams = (searchParams) => {
      return [...searchParams.entries()].reduce((acc, [key, val]) => {
        try {
          return {
            ...acc,
            [key]: JSON.parse(val)
          };
        } catch {
          return {
            ...acc,
            [key]: val
          };
        }
      }, {});
    };
    

    This way, you don't have to memoize them. And you can use them like below, for example:

    function HomePage() {
      const [searchParams, setSearchParams] = useSearchParams();
    
      function handleQueryParamsChange() {
        const filters = {
          name: "user1",
          region: {
            city: "Sydney",
            country: "Australia"
          }
        };
    
        const params = {
          filters: JSON.stringify(filters),
          anotherField: "Simple String"
        };
    
        setSearchParams(encodeSearchParams(params));
      }
    
      console.log(decodeSearchParams(searchParams).filters);
      console.log(decodeSearchParams(searchParams).anotherField);
    
      return (
        <div>
          <button onClick={handleQueryParamsChange} className="page">
            Update query params
          </button>
        </div>
      );
    }