Next.js Discord

Discord Forum

Trying to create a dropdown with buttons that update query params

Answered
berkserbet posted this in #help-forum
Open in Discord
Avatar
Hey all, struggling to do a simple thing. I have a client side component with a dropdown where users can choose how they sort a list. I want the dropdown to have two options "Newest" and "Cheapest". Whichever one is selected should stay highlighted. When user selects other one highlight should switch. User shouldn't be able to unselect.

My code has some checkboxes, I want those to become buttons.
'use client'

import React from 'react'
import { useRouter } from 'next/navigation';
import { init } from 'next/dist/compiled/webpack/webpack';

interface Props {
    sort: string
}

const SortContainer = ({sort}: Props) => {

    var sortOptions = ['Newest', 'Cheapest']
      

    const router = useRouter();
    let queryParams: URLSearchParams;

    function handleClick(checkbox: any) {
        if (typeof window !== "undefined") {
          queryParams = new URLSearchParams(window.location.search);
        }
        queryParams.set(checkbox.name, checkbox.value);
        queryParams.delete("pages");
        const path = window.location.pathname + "?" + queryParams.toString();
        router.push(path, { scroll: false });
      }


    return (
        <div className="dropdown">

            <div className="dropdown dropdown-bottom">
                <div tabIndex={0} role="button" className="flex font-light text-xs md:text-sm btn btn-sm btn-ghost">
                    Sort
                    <svg className="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
                        <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
                    </svg>
                </div>
                <ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-28">
                    {sortOptions.map(sort => 
                        <div key={sort} className="form-control">
                            <label className="label cursor-pointer">
                            <input
                                name="sort"
                                type="checkbox"
                                id="sort"
                                value={sort}
                                className="checkbox checkbox-sm"
                                onClick={(e) => handleClick(e.target)}
                            />
                            <span className="label-text ml-1 mr-auto">{sort}</span>
                            </label>
                        </div>
                    )}
                </ul>
            </div>

        </div>
    )
}

export default SortContainer
Answered by <Milind ツ />
basic implmentation:
import Link from "next/link";

const Page = ({ searchParams }) => {
   const mode = searchParams.mode;

   return (
      <>
         <div>
            <Link
               tabIndex={mode === "newest" ? -1 : undefined}
               className={`${mode == "newest" ? "bg-active pointer-events-none" : ""}`}
               href={{
                  pathname: "/test",
                  query: {
                     mode: "newest",
                  },
               }}
            >
               Newest
            </Link>
            <Link
               tabIndex={mode === "newest" ? -1 : undefined}
               className={`${mode == "cheapest" ? "bg-active pointer-events-none" : ""}`}
               href={{
                  pathname: "/test",
                  query: {
                     mode: "cheapest",
                  },
               }}
            >
               Cheapest
            </Link>
         </div>

         {mode == "cheapest" ? "data for cheapest" : "data for newest"}
      </>
   );
};

export default Page;
View full answer

7 Replies

Avatar
since u are using app router, it would be much practical to use server component. You can acheive the same result with page search params, next link, and setting the active state on active link and disabling its pointer events
Avatar
basic implmentation:
import Link from "next/link";

const Page = ({ searchParams }) => {
   const mode = searchParams.mode;

   return (
      <>
         <div>
            <Link
               tabIndex={mode === "newest" ? -1 : undefined}
               className={`${mode == "newest" ? "bg-active pointer-events-none" : ""}`}
               href={{
                  pathname: "/test",
                  query: {
                     mode: "newest",
                  },
               }}
            >
               Newest
            </Link>
            <Link
               tabIndex={mode === "newest" ? -1 : undefined}
               className={`${mode == "cheapest" ? "bg-active pointer-events-none" : ""}`}
               href={{
                  pathname: "/test",
                  query: {
                     mode: "cheapest",
                  },
               }}
            >
               Cheapest
            </Link>
         </div>

         {mode == "cheapest" ? "data for cheapest" : "data for newest"}
      </>
   );
};

export default Page;
Answer
Avatar
Thank you! Would this keep existing query params in the url?
Avatar
It doesn't seem to stack query parameters or keep the current selection highlighted
Avatar
I didn't add any css to make the button actually active css wise. bg-active is a dummy, not actual tailwind class
Plus to stack up existing query params along with mode, you have to add that param to query object
mode: cheapest,
other: searchParams.other