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:
@@ -10,6 +10,7 @@ import type { Column } from '../Table/index.js'
|
|||||||
import { useConfig } from '../../providers/Config/index.js'
|
import { useConfig } from '../../providers/Config/index.js'
|
||||||
import { usePreferences } from '../../providers/Preferences/index.js'
|
import { usePreferences } from '../../providers/Preferences/index.js'
|
||||||
import { useServerFunctions } from '../../providers/ServerFunctions/index.js'
|
import { useServerFunctions } from '../../providers/ServerFunctions/index.js'
|
||||||
|
import { abortAndIgnore } from '../../utilities/abortAndIgnore.js'
|
||||||
|
|
||||||
export interface ITableColumns {
|
export interface ITableColumns {
|
||||||
columns: Column[]
|
columns: Column[]
|
||||||
@@ -77,25 +78,36 @@ export const TableColumnsProvider: React.FC<Props> = ({
|
|||||||
const { getPreference, setPreference } = usePreferences()
|
const { getPreference, setPreference } = usePreferences()
|
||||||
|
|
||||||
const [tableColumns, setTableColumns] = React.useState(columnState)
|
const [tableColumns, setTableColumns] = React.useState(columnState)
|
||||||
|
const tableStateControllerRef = React.useRef<AbortController>(null)
|
||||||
|
|
||||||
const moveColumn = useCallback(
|
const moveColumn = useCallback(
|
||||||
async (args: { fromIndex: number; toIndex: number }) => {
|
async (args: { fromIndex: number; toIndex: number }) => {
|
||||||
|
abortAndIgnore(tableStateControllerRef.current)
|
||||||
|
|
||||||
const { fromIndex, toIndex } = args
|
const { fromIndex, toIndex } = args
|
||||||
const withMovedColumn = [...tableColumns]
|
const withMovedColumn = [...tableColumns]
|
||||||
const [columnToMove] = withMovedColumn.splice(fromIndex, 1)
|
const [columnToMove] = withMovedColumn.splice(fromIndex, 1)
|
||||||
withMovedColumn.splice(toIndex, 0, columnToMove)
|
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,
|
collectionSlug,
|
||||||
columns: sanitizeColumns(withMovedColumn),
|
columns: sanitizeColumns(withMovedColumn),
|
||||||
docs,
|
docs,
|
||||||
enableRowSelections,
|
enableRowSelections,
|
||||||
renderRowTypes,
|
renderRowTypes,
|
||||||
|
signal: controller.signal,
|
||||||
tableAppearance,
|
tableAppearance,
|
||||||
})
|
})
|
||||||
|
|
||||||
setTableColumns(columnState)
|
if (result) {
|
||||||
setTable(Table)
|
setTableColumns(result.state)
|
||||||
|
setTable(result.Table)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
tableColumns,
|
tableColumns,
|
||||||
@@ -111,24 +123,55 @@ export const TableColumnsProvider: React.FC<Props> = ({
|
|||||||
|
|
||||||
const toggleColumn = useCallback(
|
const toggleColumn = useCallback(
|
||||||
async (column: string) => {
|
async (column: string) => {
|
||||||
const toggledColumns: Pick<Column, 'accessor' | 'active'>[] = tableColumns.map((col) => {
|
abortAndIgnore(tableStateControllerRef.current)
|
||||||
return {
|
|
||||||
accessor: col.accessor,
|
|
||||||
active: col.accessor === column ? !col.active : col.active,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
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,
|
collectionSlug,
|
||||||
columns: toggledColumns,
|
columns: toggledColumns,
|
||||||
docs,
|
docs,
|
||||||
enableRowSelections,
|
enableRowSelections,
|
||||||
renderRowTypes,
|
renderRowTypes,
|
||||||
|
signal: controller.signal,
|
||||||
tableAppearance,
|
tableAppearance,
|
||||||
})
|
})
|
||||||
|
|
||||||
setTableColumns(columnState)
|
if (result) {
|
||||||
setTable(Table)
|
setTableColumns(result.state)
|
||||||
|
setTable(result.Table)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
tableColumns,
|
tableColumns,
|
||||||
@@ -233,6 +276,12 @@ export const TableColumnsProvider: React.FC<Props> = ({
|
|||||||
sortColumnProps,
|
sortColumnProps,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
abortAndIgnore(tableStateControllerRef.current)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableColumnContext.Provider
|
<TableColumnContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
Reference in New Issue
Block a user