I am using Tanstack react table in my NextJS application.
I have implemented a search field with globalFilter, worked fine.
I want to manage the state in the URL in stead of the state so search results and views are shareable between users of the app.
I have managed to do so but am fairly sure i am doing it wrong as I am managing state now in the state AND the URL. Should be only URL. I am confused if this is possible at all as you have to bind a state to the table but wonder if someone might know a solution.
This is my table:
'use client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import {
ColumnDef,
ColumnFiltersState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table'
import { set } from 'date-fns'
import { useSearchParams, useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData>[] | null
data: TData[] | null
}
export const DataTable = <TData extends TValue, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) => {
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [globalFilter, setGlobalFilter] = useState<string>('')
const searchParams = useSearchParams()
const router = useRouter()
const q = searchParams.get('q') || ''
const table = useReactTable({
data: data || [],
columns: columns || [],
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
columnFilters,
globalFilter,
},
onGlobalFilterChange: setGlobalFilter,
})
useEffect(() => {
setGlobalFilter(q)
}, [globalFilter, q])
//TODO i think global filter should be handled by the url only not also state
return (
<div>
<div className='flex items-center justify-end pb-4 '>
<Input
placeholder='Search...'
value={globalFilter}
onChange={(e) => {
setGlobalFilter(e.target.value)
router.push(`?q=${e.target.value}`, { scroll: false })
}}
className='max-w-sm w-[250px]'
/>
</div>
<div>
// table omitted for readability.
</div>
<div className='flex items-center justify-end space-x-2 py-4'>
// pagination omitted for readability
</div>
</div>
)
}
Try binding globalFilter to your url searchParams in table config Object like so:
state: { columnFilters, globalFilter: q, },
Then simply remove the [globalFilter, setGlobalFilter]
useState() call and any related code (remove onGlobalFilterChange from table config, remove useEffect, remove setGlobalFilter from onChange handler, change Input value to value={q})