Dropdown closes after each checkbox click
Answered
berkserbet posted this in #help-forum
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
ProductBrowser.tsx
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(
}
}
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()});}
}
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 You’ll need to be more specific than button clicks didn’t work
The checkboxes I mean, sorry
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
@Plott Hound Yeah. Checkboxes should really be in client files since they rely on user interaction
Is it better to do
router.push(path) then?@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(
}
}
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 '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()}`);
}
}
Got it, do I need to just move the submit function to client?
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.