From 129fadfd2ca355ee3ca3f6e947f2cdc23f53dc04 Mon Sep 17 00:00:00 2001 From: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:58:49 -0500 Subject: [PATCH] fix: wires up abort controller logic for list columns (#9180) ### What? List column state could become out of sync if toggling columns happened in rapid succession as seen in CI. Or when using a spotty connection where responses could come back out of order. ### Why? State was not being preserved between toggles. Leading to incorrect columns being toggled on/off. ### How? Updates internal column state before making the request to the server so when a future toggle occurs it has up to date state of all columns. Also introduces an abort controller to prevent the out of order response issue. --- .../ui/src/elements/TableColumns/index.tsx | 73 ++++++++++++++++--- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/packages/ui/src/elements/TableColumns/index.tsx b/packages/ui/src/elements/TableColumns/index.tsx index f0faf02864..d2f2f9ced3 100644 --- a/packages/ui/src/elements/TableColumns/index.tsx +++ b/packages/ui/src/elements/TableColumns/index.tsx @@ -10,6 +10,7 @@ import type { Column } from '../Table/index.js' import { useConfig } from '../../providers/Config/index.js' import { usePreferences } from '../../providers/Preferences/index.js' import { useServerFunctions } from '../../providers/ServerFunctions/index.js' +import { abortAndIgnore } from '../../utilities/abortAndIgnore.js' export interface ITableColumns { columns: Column[] @@ -77,25 +78,36 @@ export const TableColumnsProvider: React.FC = ({ const { getPreference, setPreference } = usePreferences() const [tableColumns, setTableColumns] = React.useState(columnState) + const tableStateControllerRef = React.useRef(null) const moveColumn = useCallback( async (args: { fromIndex: number; toIndex: number }) => { + abortAndIgnore(tableStateControllerRef.current) + const { fromIndex, toIndex } = args const withMovedColumn = [...tableColumns] const [columnToMove] = withMovedColumn.splice(fromIndex, 1) withMovedColumn.splice(toIndex, 0, columnToMove) - const { state: columnState, Table } = await getTableState({ + setTableColumns(withMovedColumn) + + const controller = new AbortController() + tableStateControllerRef.current = controller + + const result = await getTableState({ collectionSlug, columns: sanitizeColumns(withMovedColumn), docs, enableRowSelections, renderRowTypes, + signal: controller.signal, tableAppearance, }) - setTableColumns(columnState) - setTable(Table) + if (result) { + setTableColumns(result.state) + setTable(result.Table) + } }, [ tableColumns, @@ -111,24 +123,55 @@ export const TableColumnsProvider: React.FC = ({ const toggleColumn = useCallback( async (column: string) => { - const toggledColumns: Pick[] = tableColumns.map((col) => { - return { - accessor: col.accessor, - active: col.accessor === column ? !col.active : col.active, - } - }) + abortAndIgnore(tableStateControllerRef.current) - const { state: columnState, Table } = await getTableState({ + const { newColumnState, toggledColumns } = tableColumns.reduce<{ + newColumnState: Column[] + toggledColumns: Pick[] + }>( + (acc, col) => { + if (col.accessor === column) { + acc.newColumnState.push({ + ...col, + accessor: col.accessor, + active: !col.active, + }) + acc.toggledColumns.push({ + accessor: col.accessor, + active: !col.active, + }) + } else { + acc.newColumnState.push(col) + acc.toggledColumns.push({ + accessor: col.accessor, + active: col.active, + }) + } + + return acc + }, + { newColumnState: [], toggledColumns: [] }, + ) + + setTableColumns(newColumnState) + + const controller = new AbortController() + tableStateControllerRef.current = controller + + const result = await getTableState({ collectionSlug, columns: toggledColumns, docs, enableRowSelections, renderRowTypes, + signal: controller.signal, tableAppearance, }) - setTableColumns(columnState) - setTable(Table) + if (result) { + setTableColumns(result.state) + setTable(result.Table) + } }, [ tableColumns, @@ -233,6 +276,12 @@ export const TableColumnsProvider: React.FC = ({ sortColumnProps, ]) + React.useEffect(() => { + return () => { + abortAndIgnore(tableStateControllerRef.current) + } + }, []) + return (