I'm currently learning NextJS and React, and I'm stuck trying to figure out how to make an input search filter to filter a table.
I've seen a lot of videos and comments about using useSearchParams, but it's not the thing I'd like to do.
I'd like to filter in real-time, making the table refresh every time the user presses a key on the input and seeing the number of rows decrease as I write more in the input filter.
What should I do?
This is my search input file:
"use client"
import { Input } from "@/components/ui/input"
import { useState} from 'react';
export default function InputSearch() {
const [search, setSearch] = useState('')
return(
<Input
placeholder="Buscar categoría..."
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
)
}
And this is my page.tsx file (server component)
import Navbar from "@/components/navbar";
import {Heading} from "@/components/ui/heading"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import {Button} from "@/components/ui/button"
import {db} from "@/lib/db"
import InputSearch from "@/app/table/categorias/components/search-input";
const SearchCategoryPage = async() => {
const categorias = await db.table_Categories.findMany({
orderBy: {
name: "asc",
}
})
const subcategorias = await db.table_Subcategories.findMany({
orderBy: {
name: "asc",
}
})
return(
<div>
<Navbar></Navbar>
<Heading
title="Categorías"
description="Description"
/>
<div className="pl-4 pr-4 w-[100%]">
<InputSearch/>
<Accordion type="single" collapsible className="w-full">
{categorias
.map((filteredCategoria) => (
<AccordionItem key={filteredCategoria.id} value={filteredCategoria.name}>
<AccordionTrigger>{filteredCategoria.name}</AccordionTrigger>
<AccordionContent>
{subcategorias
.filter((subcategoria) => subcategoria.id_category === filteredCategoria.id)
.map((filteredSubcategoria) => (
<div>
<Button variant="link" key={filteredSubcategoria.id}>{filteredSubcategoria.name}</Button>
</div>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</div>
)
}
export default SearchCategoryPage
There are several ways to implement this approach. One of them is to use state management like useContext
or redux
to get the user's input search value and pass to your desired component. However, I recommend using URL search parameters, which means converting the user's input query into URL search query. This way, the current page can directly access this variable. This method is also recommended by the Next.js official documentation. You can refer to https://nextjs.org/learn/dashboard-app/adding-search-and-pagination
// InputSearch
"use client"
import { Input } from "@/components/ui/input"
import { useSearchParams } from 'next/navigation'
export default function InputSearch() {
const params = new URLSearchParams(searchParams)
return(
<Input
placeholder="Buscar categoría..."
onChange={(e) => {
if (e.target.value) {
params.set('query', e.target.value);
} else {
params.delete('query');
}
}}
value={params.query}
/>
)
}
// SearchCategoryPage
.
.
.
import InputSearch from "@/app/table/categorias/components/search-input";
const SearchCategoryPage = async({searchParams}) => {
const query = searchParams?.query || '';
const categorias = await db.table_Categories.findMany({
orderBy: {
name: "asc",
},
// do your search query...
})
const subcategorias = await db.table_Subcategories.findMany({
orderBy: {
name: "asc",
},
// do your search query...
})
return(
<div>
<Navbar></Navbar>
<Heading
title="Categorías"
description="Description"
/>
<div className="pl-4 pr-4 w-[100%]">
<InputSearch/>
<Accordion type="single" collapsible className="w-full">
{categorias
.map((filteredCategoria) => (
<AccordionItem key={filteredCategoria.id} value={filteredCategoria.name}>
<AccordionTrigger>{filteredCategoria.name}</AccordionTrigger>
<AccordionContent>
{subcategorias
.filter((subcategoria) => subcategoria.id_category === filteredCategoria.id)
.map((filteredSubcategoria) => (
<div>
<Button variant="link" key={filteredSubcategoria.id}>{filteredSubcategoria.name}</Button>
</div>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
</div>
)
}
export default SearchCategoryPage