I have a tanstack table in NextJS and it works perfectly in dev. However, in prod, it gives a client-side exception:
framework-3b5a00d5d7e8d93b.js:9 TypeError: Cannot read properties of undefined (reading 'filter')
at 338-40b21a5eb2cada72.js:42:10338
at Object.getVisibleLeafColumns (338-40b21a5eb2cada72.js:39:249)
at 338-40b21a5eb2cada72.js:42:5838
at Object.getHeaderGroups (338-40b21a5eb2cada72.js:39:249)
at u (index-ef857841339be48b.js:1:1605)
at ak (framework-3b5a00d5d7e8d93b.js:9:60917)
at i (framework-3b5a00d5d7e8d93b.js:9:119475)
at oD (framework-3b5a00d5d7e8d93b.js:9:99114)
at framework-3b5a00d5d7e8d93b.js:9:98981
at oO (framework-3b5a00d5d7e8d93b.js:9:98988)
a9 @ framework-3b5a00d5d7e8d93b.js:9
main-f2e125da23ccdc4a.js:1 TypeError: Cannot read properties of undefined (reading 'filter')
at 338-40b21a5eb2cada72.js:42:10338
at Object.getVisibleLeafColumns (338-40b21a5eb2cada72.js:39:249)
at 338-40b21a5eb2cada72.js:42:5838
at Object.getHeaderGroups (338-40b21a5eb2cada72.js:39:249)
at u (index-ef857841339be48b.js:1:1605)
at ak (framework-3b5a00d5d7e8d93b.js:9:60917)
at i (framework-3b5a00d5d7e8d93b.js:9:119475)
at oD (framework-3b5a00d5d7e8d93b.js:9:99114)
at framework-3b5a00d5d7e8d93b.js:9:98981
at oO (framework-3b5a00d5d7e8d93b.js:9:98988)
$ @ main-f2e125da23ccdc4a.js:1
main-f2e125da23ccdc4a.js:1 A client-side exception has occurred, see here for more info: https://nextjs.org/docs/messages/client-side-exception-occurred
I've looked at my code and examples provided, and I can't see the issue. Here is my table:
import React, { useEffect, useMemo, useState } from 'react'
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from '@tanstack/react-table'
// Styles
import styles from '../styles/ResultsTable.module.scss'
// Types
import { Row, Table } from '../types/tableTypes'
const ResultsTable = ({items, limit = items.length < 100 ? items.length : 100, totalWords}: Table) => {
// State
const [results, setResults] = useState<Row[]>([])
const [rowsPerPage, setRowsPerPage] = useState<number>(20)
const [sorting, setSorting] = useState<SortingState>([])
const [topSize, setTopSize] = useState(limit > items.length ? items.length : limit)
const [topSizeInput, setTopSizeInput] = useState<number>(limit > items.length ? items.length : limit ?? 0)
// Regex matcher
const numMatchRegex = new RegExp(/^[1-9][0-9]*$/g)
// Functions
const handleTopSize = ({target: { value }}: React.BaseSyntheticEvent) => {
// Handle error cases (non-number and larger/smaller than size)
if (value != '' && !value.match(numMatchRegex)) return
setTopSizeInput(value)
}
const handleTopSizeSubmit = () => {
// If it's too high or low
if (topSizeInput > items.length) {
setTopSizeInput(items.length)
setTopSize(items.length)
return
}
if (topSizeInput <= 0) {
setTopSize(1)
setTopSizeInput(1)
}
console.log('not too high or low')
setTopSizeInput(topSizeInput)
setTopSize(topSizeInput)
}
const columns = useMemo<ColumnDef<Row>[]>(() => [
{
id: 'Word',
accessorFn: info => info[0],
cell: info => info.getValue(),
header: () => 'Word'
},
{
id: 'Occurence',
accessorFn: info => info[1],
cell: info => info.getValue(),
header: () => 'Occurence'
}
], [])
// Table
const table = useReactTable({
data: results ?? [],
columns,
onSortingChange: setSorting,
state: {
sorting
},
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
useEffect(() => {
// Set results to specified limit and slice when topSize changes
setResults(items.slice(0, topSize <= items.length ? topSize : items.length - 1))
if (items.length < topSize) setTopSize(items.length)
}, [items, topSize])
return (
<div className='slim'>
<div className={styles.topLine}>
{!!totalWords && <p data-cy="num-results">Total Words: {totalWords}</p>}
<p data-cy="top-num-title">Top {topSize} words</p>
</div>
{results && (
<div>
<table data-cy="results-table">
<thead>
{!!table?.getHeaderGroups() && table?.getHeaderGroups()?.map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup?.headers?.map(header => {
return (
<th key={header?.id} colSpan={header.colSpan}>
{header?.isPlaceholder ? null : (
<div
{ ...{
className: header.column.getCanSort() ? 'can-sort' : '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header?.getContext(),
)},
{{
asc: ' 🔼',
desc: ' 🔽',
}[header?.column?.getIsSorted() as string] ?? null
}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody>
{table
.getRowModel()
?.rows?.slice(0, rowsPerPage)
?.map((row, idx) => {
return (
<tr key={row.id} className={idx % 2 === 0 ? 'left-content' : 'right-content'}>
{row.getVisibleCells()?.map(cell => {
return (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</td>
)
})
}
</tr>
)
})
}
</tbody>
</table>
<div className="h-2" />
<div className="flex items-center gap-2 table-buttons">
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
{'<<'}
</button>
<button
className="border rounded p-1"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
{'<'}
</button>
<button
className="border rounded p-1"
onClick={() => table.nextPage()}
disabled={!table?.getCanNextPage()}
>
{'>'}
</button>
<button
className="border rounded p-1"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table?.getCanNextPage()}
>
{'>>'}
</button>
</div>
<div className="flex-row space-between" style={{marginTop: '3rem'}}>
<div className="flex-column">
<label>Num. of words to show:</label>
<input data-cy="top-num-changer" placeholder={topSize} onChange={handleTopSize} value={topSizeInput ?? ''} />
</div>
<button data-cy="top-num-submit" style={{justifySelf: 'flex-end'}} onClick={() => setTopSize(handleTopSizeSubmit)}>Change</button>
</div>
</div>
)}
</div>
)
}
export default ResultsTable
It was an issue with Next 13's buggy minifier. Updating to the latest Next 13 will fix the issue.
The issue that I created (with the intermediary fix if you're on the offending version of Next 13)