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.
This commit is contained in:
Jarrod Flesch
2024-11-13 14:58:49 -05:00
committed by GitHub
parent cea7d58d96
commit 129fadfd2c

View File

@@ -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<Props> = ({
const { getPreference, setPreference } = usePreferences()
const [tableColumns, setTableColumns] = React.useState(columnState)
const tableStateControllerRef = React.useRef<AbortController>(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<Props> = ({
const toggleColumn = useCallback(
async (column: string) => {
const toggledColumns: Pick<Column, 'accessor' | 'active'>[] = 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<Column, 'accessor' | 'active'>[]
}>(
(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<Props> = ({
sortColumnProps,
])
React.useEffect(() => {
return () => {
abortAndIgnore(tableStateControllerRef.current)
}
}, [])
return (
<TableColumnContext.Provider
value={{