Next.js Discord

Discord Forum

Dropdown closes after each checkbox click

Answered
berkserbet posted this in #help-forum
Open in Discord
Every time after I click a checkbox option, the dropdown closes and it feels like the entire site refreshes. This also doesn't happen very fast. Feels like almost a second.

I want the dropdown to stay open and for the site to feel more dynamic where I can check many checkboxes in order. I would really appreciate some help with that!

The checkboxes are on a server side component of a one page app. Will share some simplified code below

Page.tsx
import React from 'react'
import ProductBroswer from './ProductBroswer';
import { ProductSearchParams } from '@/types/products';

interface Props {
  searchParams: ProductSearchParams
}

export default function Home({ searchParams }: Props) {
  return (
    <main>
      <ProductBroswer key={JSON.stringify(searchParams)} {...searchParams} />
    </main>
  );
}


ProductBrowser.tsx
import .. // all imports


function sanitize(params: ProductSearchParams) {
  const stringsOnly = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v]));
  return {... stringsOnly, pages: Number(stringsOnly.pages)} as Record<Exclude<ProductSearchParam, 'pages'>, string> & { pages: number };
}

const ProductBroswer = (props: ProductSearchParams) => {
  const initialParams = sanitize(props)

  // some product filtering

  async function submitForm(formData: FormData) {
    'use server';
    const newSearchParams = new URLSearchParams();
    allSearchParams.forEach(p => {
      const opts = formData.getAll(p)
      if (opts.length) {
        newSearchParams.append(p, opts.join(","));
      }
    })
    if (initialParams.search) {
      newSearchParams.append("search", initialParams.search)
    }
    redirect(`?${newSearchParams.toString()}`)
  }
  
  function checkHandler(param: ProductSearchParam, values: any) {
    if (!initialParams[param] || param === 'pages') {
      return false;
    }
    const options = initialParams[param].split(',')
    switch (param) {
      case "r": //reduced for simplisity
        return options.includes(values['subreddit_name']);
      case "genders":
        return options.includes(values['country']);
      default:
        return false;
    }
  }

  return (
    <form action={submitForm}>
      <div className="flex justify-center">
        <p className="items-center flex text-sm md:text-md">For-sale in {initialParams.r && "r/"}</p>
        <div className="dropdown dropdown-bottom dropdown-end">
          {!initialParams.r && <div tabIndex={0} role="button" className="btn btn-sm m-1 btn-secondary btn-outline">All Supported Subreddits</div>}
          {initialParams.r && initialParams.r.split(",").length < 3 && <div tabIndex={0} role="button" className="btn btn-sm m-1 btn-secondary">{initialParams.r.split(",").join(", ")}</div>}
          {initialParams.r && initialParams.r.split(",").length >= 3 && <div tabIndex={0} role="button" className="btn btn-sm m-1 btn-secondary">{initialParams.r.split(",").slice(0,2).join(", ")}...</div>}
          <ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-44">
            {subreddit_options.map(subreddit_name => 
              <div key={subreddit_name} className="form-control">
                <label className="label cursor-pointer">
                  <span className="label-text">{subreddit_name}</span>
                  <CheckBox
                    name="r"
                    type="checkbox"
                    id="r"
                    value={subreddit_name}
                    className="checkbox checkbox-sm"
                    checked={checkHandler("r", {subreddit_name})}
                  />
                </label>
              </div>
            )}
          </ul>
        </div>
      </div>
      // other drowdowns / checkboxes and products list
    </form>
Answered by Plott Hound
'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';

export default function Search() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();

function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(${pathname}?${params.toString()});
}
}
View full answer

27 Replies

Plott Hound
Your redirect is causing the page to refresh and close the dropdown
I think it would be better to handle changing the url with replace from useRouter in the client
@Plott Hound I think it would be better to handle changing the url with replace from useRouter in the client
But I was told to move to redirect because sometimes the button clicks didn't work
@berkserbet But I was told to move to redirect because sometimes the button clicks didn't work
Plott Hound
You’ll need to be more specific than button clicks didn’t work
Redirecting to the same page will reload it so that’s why your dropdown is closing
Plott Hound
Just to make sure I’m not wrong can you comment out the redirect line and try toggling the checkboxes?
It clicks but doesn't update
When I make them client side some checkbox clicks don't seem to work - is that normal?
Plott Hound
Yeah. Checkboxes should really be in client files since they rely on user interaction
@berkserbet Is it better to do `router.push(path)` then?
Plott Hound
Give it a try but I’m pretty sure you’ll get the same result
@Plott Hound Give it a try but I’m pretty sure you’ll get the same result
Same result because it's on server side right?
Plott Hound
I think so yeah
Plott Hound
'use client';

import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';

export default function Search() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();

function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(${pathname}?${params.toString()});
}
}
Answer
Plott Hound
This would be the ideal way to update the url
Sorry it’s different I just pasted it from the docs
Plott Hound
Not at my pc
@berkserbet Got it, do I need to just move the submit function to client?
Plott Hound
Yeah that will work
Sorry I'm new to this
@Plott Hound Yeah that will work
Cool will try, thank you!
Plott Hound
No problem. It’s confusing at first
@berkserbet Cool will try, thank you!
Plott Hound
Thanks. If you could mark my post as the solution that would be appreciated.