T
TanStack16mo ago
national-gold

In a table with inputs as cell, how to render only concerned rows instead of the entire table ?

When i change the value inside of a cell, the entire table gets rendered. How can i render only the row concerned by the change ?
const InviteUsersTable = ({data, removeInvite, handleRoleChange, handleEmailChange}: Props) => {

const columnsHelper = createColumnHelper<UserInvite>()

const columns = useMemo(() => [
columnsHelper.accessor('email', {
header: () => <div><Text size="sm" weight="semibold">Email</Text></div>,
cell: ({row, getValue}) => (
<TextInput
aria-labelledby={row.original.email}
size='sm'
value={getValue()}
onChange={(value) => handleEmailChange(row.original.email, value)}
/>
)
}),
columnsHelper.accessor('admin', {
header: () => <HeaderCellTooltip message="Admin can do anything">Admin</HeaderCellTooltip>,
cell: ({ row, getValue }) => (
<input
aria-labelledby={`role-${row.original.email}-admin`}
type="radio"
id={`role-${row.original.email}-admin`}
onChange={(e) => handleRoleChange(row.original.email, 'admin')}
checked={data.find((user => user.email === row.original.email)).role === "admin"}
/>
)
}),
columnsHelper.accessor('viewer', {
header: () => <HeaderCellTooltip message="Viewer can only view">Viewer</HeaderCellTooltip>,
cell: ({ row, getValue }) => (
<input
aria-labelledby={`role-${row.original.email}-viewer`}
type="radio"
id={`role-${row.original.email}-viewer`}
onChange={(e) => handleRoleChange(row.original.email, 'viewer')}
checked={data.find((user => user.email === row.original.email)).role === "viewer"}
/>
)
}),
columnsHelper.accessor('action', {
header: () => null,
cell: info => <Button onClick={() => removeInvite(Number(info.row.id))}><Icon size="md" name="x"/></Button>
})
], [data])

const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})

return (
<div className="p-2">
<table>
<thead className="border-b border-neutral-300">
{table.getHeaderGroups().map((headerGroup) => (
<tr className="p-2" key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th className="text-left box-border px-3" key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody className="overflow-y-scroll max-h-[50px]">
{table.getRowModel().rows.map((row) => (
<tr className="p-2" key={row.id}>
{row.getVisibleCells().map((cell) => (
<td className="text-left box-border px-3 py-2" key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
}
const InviteUsersTable = ({data, removeInvite, handleRoleChange, handleEmailChange}: Props) => {

const columnsHelper = createColumnHelper<UserInvite>()

const columns = useMemo(() => [
columnsHelper.accessor('email', {
header: () => <div><Text size="sm" weight="semibold">Email</Text></div>,
cell: ({row, getValue}) => (
<TextInput
aria-labelledby={row.original.email}
size='sm'
value={getValue()}
onChange={(value) => handleEmailChange(row.original.email, value)}
/>
)
}),
columnsHelper.accessor('admin', {
header: () => <HeaderCellTooltip message="Admin can do anything">Admin</HeaderCellTooltip>,
cell: ({ row, getValue }) => (
<input
aria-labelledby={`role-${row.original.email}-admin`}
type="radio"
id={`role-${row.original.email}-admin`}
onChange={(e) => handleRoleChange(row.original.email, 'admin')}
checked={data.find((user => user.email === row.original.email)).role === "admin"}
/>
)
}),
columnsHelper.accessor('viewer', {
header: () => <HeaderCellTooltip message="Viewer can only view">Viewer</HeaderCellTooltip>,
cell: ({ row, getValue }) => (
<input
aria-labelledby={`role-${row.original.email}-viewer`}
type="radio"
id={`role-${row.original.email}-viewer`}
onChange={(e) => handleRoleChange(row.original.email, 'viewer')}
checked={data.find((user => user.email === row.original.email)).role === "viewer"}
/>
)
}),
columnsHelper.accessor('action', {
header: () => null,
cell: info => <Button onClick={() => removeInvite(Number(info.row.id))}><Icon size="md" name="x"/></Button>
})
], [data])

const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})

return (
<div className="p-2">
<table>
<thead className="border-b border-neutral-300">
{table.getHeaderGroups().map((headerGroup) => (
<tr className="p-2" key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th className="text-left box-border px-3" key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</th>
))}
</tr>
))}
</thead>
<tbody className="overflow-y-scroll max-h-[50px]">
{table.getRowModel().rows.map((row) => (
<tr className="p-2" key={row.id}>
{row.getVisibleCells().map((cell) => (
<td className="text-left box-border px-3 py-2" key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
}
4 Replies
genetic-orange
genetic-orange16mo ago
That's just how React works. Another way of looking at this - when the data in this cell changes, how can the app be sure that all filters, sorting, pagination, etc. are updated predictably? - Re-rendering is the only answer here. This only becomes an actual issue if the performance of your app slows significantly. Only then is there a need to change up your approach. - Limit rows, number of columns - Use virtualisation, etc. A good starting point on this topic is this article: https://kentcdodds.com/blog/fix-the-slow-render-before-you-fix-the-re-render
Fix the slow render before you fix the re-render
How to start optimizing your React app renders
national-gold
national-goldOP16mo ago
Thanks for the tip, I will focus on the main problem which is not the performance of my table
fair-rose
fair-rose16mo ago
I personally made a solution which only turns the cell into an input on click then focuses the input. A strategy like this may work well for your problem aswell.
national-gold
national-goldOP16mo ago
Interesting @mtti.gg , for now i made a solution that consists of putting a ref on the input element and triggering a focus event on that ref when the table state changes, but i will keep your solution in mind for a future refactoring

Did you find this page helpful?