From 741efd78afca1f383fe43e3e1e67b825d4a2dd7a Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 29 Jun 2025 20:18:34 +1000 Subject: [PATCH] Fix for caching table options (#9898) - The sync: false option does not perform properly - Sometimes, the data is simply not loaded from localStorage at all --- src/frontend/src/hooks/UseTable.tsx | 96 +++++++++++++++------- src/frontend/src/tables/InvenTreeTable.tsx | 7 ++ 2 files changed, 75 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/hooks/UseTable.tsx b/src/frontend/src/hooks/UseTable.tsx index ab5ea34347..19928c5d0e 100644 --- a/src/frontend/src/hooks/UseTable.tsx +++ b/src/frontend/src/hooks/UseTable.tsx @@ -6,6 +6,12 @@ import type { FilterSetState } from '@lib/types/Filters'; import type { TableState } from '@lib/types/Tables'; import { useFilterSet } from './UseFilterSet'; +// Interface for the stored table data in local storage +interface StoredTableData { + pageSize: number; + hiddenColumns: string[] | null; +} + /** * A custom hook for managing the state of an component. * @@ -65,37 +71,71 @@ export function useTable(tableName: string, idAccessor = 'pk'): TableState { // Total record count const [recordCount, setRecordCount] = useState(0); - const [pageSizeLoaded, setPageSizeLoaded] = useState(false); - - // Pagination data const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useLocalStorage({ - key: 'inventree-table-page-size', - defaultValue: 25, - sync: false, - deserialize: (value: string | undefined) => { - setPageSizeLoaded(true); - return value === undefined ? 25 : JSON.parse(value); - } + + const [storedDataLoaded, setStoredDataLoaded] = useState(false); + + const [tableData, setTableData] = useState({ + pageSize: 25, + hiddenColumns: null }); - const [hiddenColumnsLoaded, setHiddenColumnsLoaded] = - useState(false); + const [storedTableData, setStoredTableData] = + useLocalStorage({ + key: `inventree-table-data-${tableName}`, + getInitialValueInEffect: true, + // sync: false, // Do not use this option - see below + defaultValue: { + pageSize: 25, + hiddenColumns: null + }, + deserialize: (value: any) => { + const tableData = + value === undefined + ? { + pageSize: 25, + hiddenColumns: null + } + : JSON.parse(value); - // A list of hidden columns, saved to local storage - const [hiddenColumns, setHiddenColumns] = useLocalStorage({ - key: `inventree-hidden-table-columns-${tableName}`, - defaultValue: null, - sync: false, - deserialize: (value) => { - setHiddenColumnsLoaded(true); - return value === undefined ? null : JSON.parse(value); - } - }); + if (!storedDataLoaded) { + setStoredDataLoaded((wasLoaded: boolean) => { + if (!wasLoaded) { + // First load of stored table data - copy to local state + // We only do this on first load, to avoid live syncing between tabs + // Note: The 'sync: false' option is not used, it does not perform as expected + setTableData(tableData); + } + return true; + }); + } + return tableData; + } + }); - const storedDataLoaded = useMemo(() => { - return pageSizeLoaded && hiddenColumnsLoaded; - }, [pageSizeLoaded, hiddenColumnsLoaded]); + const setPageSize = useCallback((size: number) => { + setStoredTableData((prev) => ({ + ...prev, + pageSize: size + })); + setTableData((prev) => ({ + ...prev, + pageSize: size + })); + }, []); + + const setHiddenColumns = useCallback((columns: string[] | null) => { + setStoredTableData((prev) => { + return { + ...prev, + hiddenColumns: columns + }; + }); + setTableData((prev) => ({ + ...prev, + hiddenColumns: columns + })); + }, []); // Search term const [searchTerm, setSearchTerm] = useState(''); @@ -146,7 +186,8 @@ export function useTable(tableName: string, idAccessor = 'pk'): TableState { setSelectedRecords, clearSelectedRecords, hasSelectedRecords, - hiddenColumns, + pageSize: tableData.pageSize, + hiddenColumns: tableData.hiddenColumns, setHiddenColumns, searchTerm, setSearchTerm, @@ -154,7 +195,6 @@ export function useTable(tableName: string, idAccessor = 'pk'): TableState { setRecordCount, page, setPage, - pageSize, setPageSize, storedDataLoaded, records, diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index 4413226027..10fc744344 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -547,6 +547,11 @@ export function InvenTreeTable>({ return []; } + if (!tableState.storedDataLoaded) { + // Table data not yet loaded - do not load! + return []; + } + return api .get(url, { params: queryParams, @@ -610,6 +615,7 @@ export function InvenTreeTable>({ queryKey: [ 'tabledata', url, + tableState.tableKey, tableState.page, props.params, sortingLoaded, @@ -617,6 +623,7 @@ export function InvenTreeTable>({ sortStatus.direction, tableState.tableKey, tableState.filterSet.activeFilters, + tableState.storedDataLoaded, tableState.searchTerm ], enabled: !!url && !tableData && tableState.storedDataLoaded,