From 0628f2f07d70a152de63f678bcfe5aa1d70d178c Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Mon, 9 Feb 2026 16:04:39 +0800 Subject: [PATCH] [FE-2495] fix(studio): ReferenceRecordPeek breaking with JSONB ARRAY data (#42602) Issue: - When opening the peek record foreign key viewer, a full screen React error would happen Change: - Uses the preexisting formatters for the `` component To test: - Create two tables with a foreign key reference. I did `posts <- comments` - On your "posts" table, create a column with a jsonb array column and **add at least one item to the array** - On your "comments" table, open the foreign row peek and make sure no errors occur ## Summary by CodeRabbit * **Refactor** * Optimized grid component performance through improved column rendering and enhanced caching mechanisms. * Added specialized formatters for binary and JSON data types to improve display quality. * Improved memory efficiency and rendering speed, particularly when working with large datasets. * Streamlined cell rendering logic for more responsive data grids. --- .../formatter/ReferenceRecordPeek.tsx | 105 +++++++++--------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/apps/studio/components/grid/components/formatter/ReferenceRecordPeek.tsx b/apps/studio/components/grid/components/formatter/ReferenceRecordPeek.tsx index 0ac4073ff3a..6b9254f9127 100644 --- a/apps/studio/components/grid/components/formatter/ReferenceRecordPeek.tsx +++ b/apps/studio/components/grid/components/formatter/ReferenceRecordPeek.tsx @@ -1,7 +1,4 @@ import { PostgresTable } from '@supabase/postgres-meta' -import { Key } from 'lucide-react' -import DataGrid, { Column } from 'react-data-grid' - import { keepPreviousData } from '@tanstack/react-query' import { useParams } from 'common' import { COLUMN_MIN_WIDTH } from 'components/grid/constants' @@ -9,13 +6,20 @@ import { ESTIMATED_CHARACTER_PIXEL_WIDTH, getColumnDefaultWidth, } from 'components/grid/utils/gridColumns' -import { convertByteaToHex } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.utils' +import { isArrayColumn, isBinaryColumn, isJsonColumn } from 'components/grid/utils/types' import { EditorTablePageLink } from 'data/prefetchers/project.$ref.editor.$id' import { useTableRowsQuery } from 'data/table-rows/table-rows-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' -import { Button, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' +import { Key } from 'lucide-react' +import { useMemo } from 'react' +import DataGrid, { Column } from 'react-data-grid' +import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' +import { BinaryFormatter } from './BinaryFormatter' +import { DefaultFormatter } from './DefaultFormatter' +import { JsonFormatter } from './JsonFormatter' + interface ReferenceRecordPeekProps { table: PostgresTable column: string @@ -44,59 +48,52 @@ export const ReferenceRecordPeek = ({ table, column, value }: ReferenceRecordPee { placeholderData: keepPreviousData } ) - const primaryKeys = table.primary_keys.map((x) => x.name) + const primaryKeys = useMemo(() => table.primary_keys.map((x) => x.name), [table.primary_keys]) - const columns = (table?.columns ?? []).map((column) => { - const columnDefaultWidth = getColumnDefaultWidth({ - dataType: column.data_type, - format: column.format, - } as any) - const columnWidthBasedOnName = - (column.name.length + column.format.length) * ESTIMATED_CHARACTER_PIXEL_WIDTH - const columnWidth = - columnDefaultWidth < columnWidthBasedOnName ? columnWidthBasedOnName : columnDefaultWidth - const isPrimaryKey = primaryKeys.includes(column.name) + const columns = useMemo(() => { + return (table?.columns ?? []).map((column) => { + const columnDefaultWidth = getColumnDefaultWidth({ + dataType: column.data_type, + format: column.format, + } as any) + const columnWidthBasedOnName = + (column.name.length + column.format.length) * ESTIMATED_CHARACTER_PIXEL_WIDTH + const columnWidth = + columnDefaultWidth < columnWidthBasedOnName ? columnWidthBasedOnName : columnDefaultWidth + const isPrimaryKey = primaryKeys.includes(column.name) - const res: Column = { - key: column.name, - name: column.name, - resizable: false, - draggable: false, - sortable: false, - width: columnWidth, - minWidth: COLUMN_MIN_WIDTH, - headerCellClass: 'outline-none !shadow-none', - renderHeaderCell: () => ( -
- {isPrimaryKey && ( - - - - - Primary key - - )} - {column.name} - {column.format} -
- ), - renderCell: ({ column: col, row }) => { - const value = row[col.name as any] - const formattedValue = column.format === 'bytea' ? convertByteaToHex(value) : value - return ( -
= { + key: column.name, + name: column.name, + resizable: false, + draggable: false, + sortable: false, + width: columnWidth, + minWidth: COLUMN_MIN_WIDTH, + headerCellClass: 'outline-none !shadow-none', + renderHeaderCell: () => ( +
+ {isPrimaryKey && ( + + + + + Primary key + )} - > - {formattedValue === null ? 'NULL' : formattedValue} + {column.name} + {column.format}
- ) - }, - } - return res - }) + ), + renderCell: isBinaryColumn(column.data_type) + ? BinaryFormatter + : isJsonColumn(column.data_type) && !isArrayColumn(column.data_type) + ? JsonFormatter + : DefaultFormatter, + } + return res + }) + }, [table?.columns, primaryKeys]) return ( <>