Use global state for grid card views
- Prevent re-render when fetching already cached state
This commit is contained in:
parent
19eaf44394
commit
16433457ad
6 changed files with 93 additions and 12 deletions
|
@ -1,12 +1,4 @@
|
||||||
import {
|
import { useRef, useMemo, useCallback, forwardRef, Ref, useImperativeHandle } from 'react';
|
||||||
useState,
|
|
||||||
useRef,
|
|
||||||
useMemo,
|
|
||||||
useCallback,
|
|
||||||
forwardRef,
|
|
||||||
Ref,
|
|
||||||
useImperativeHandle,
|
|
||||||
} from 'react';
|
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import type { FixedSizeListProps } from 'react-window';
|
import type { FixedSizeListProps } from 'react-window';
|
||||||
import InfiniteLoader from 'react-window-infinite-loader';
|
import InfiniteLoader from 'react-window-infinite-loader';
|
||||||
|
@ -25,11 +17,13 @@ interface VirtualGridProps extends Omit<FixedSizeListProps, 'children' | 'itemSi
|
||||||
display?: ListDisplayType;
|
display?: ListDisplayType;
|
||||||
fetchFn: (options: { columnCount: number; skip: number; take: number }) => Promise<any>;
|
fetchFn: (options: { columnCount: number; skip: number; take: number }) => Promise<any>;
|
||||||
handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void;
|
handlePlayQueueAdd?: (options: PlayQueueAddOptions) => void;
|
||||||
|
itemData: any[];
|
||||||
itemGap: number;
|
itemGap: number;
|
||||||
itemSize: number;
|
itemSize: number;
|
||||||
itemType: LibraryItem;
|
itemType: LibraryItem;
|
||||||
minimumBatchSize?: number;
|
minimumBatchSize?: number;
|
||||||
route?: CardRoute;
|
route?: CardRoute;
|
||||||
|
setItemData: (data: any[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const constrainWidth = (width: number) => {
|
const constrainWidth = (width: number) => {
|
||||||
|
@ -48,6 +42,8 @@ export const VirtualInfiniteGrid = forwardRef(
|
||||||
itemSize,
|
itemSize,
|
||||||
itemType,
|
itemType,
|
||||||
cardRows,
|
cardRows,
|
||||||
|
itemData,
|
||||||
|
setItemData,
|
||||||
route,
|
route,
|
||||||
onScroll,
|
onScroll,
|
||||||
display,
|
display,
|
||||||
|
@ -60,7 +56,6 @@ export const VirtualInfiniteGrid = forwardRef(
|
||||||
}: VirtualGridProps,
|
}: VirtualGridProps,
|
||||||
ref: Ref<VirtualInfiniteGridRef>,
|
ref: Ref<VirtualInfiniteGridRef>,
|
||||||
) => {
|
) => {
|
||||||
const [itemData, setItemData] = useState<any[]>([]);
|
|
||||||
const listRef = useRef<any>(null);
|
const listRef = useRef<any>(null);
|
||||||
const loader = useRef<InfiniteLoader>(null);
|
const loader = useRef<InfiniteLoader>(null);
|
||||||
|
|
||||||
|
@ -111,7 +106,7 @@ export const VirtualInfiniteGrid = forwardRef(
|
||||||
|
|
||||||
setItemData(newData);
|
setItemData(newData);
|
||||||
},
|
},
|
||||||
[columnCount, fetchFn, itemData],
|
[columnCount, fetchFn, itemData, setItemData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const debouncedLoadMoreItems = debounce(loadMoreItems, 500);
|
const debouncedLoadMoreItems = debounce(loadMoreItems, 500);
|
||||||
|
@ -120,7 +115,7 @@ export const VirtualInfiniteGrid = forwardRef(
|
||||||
resetLoadMoreItemsCache: () => {
|
resetLoadMoreItemsCache: () => {
|
||||||
if (loader.current) {
|
if (loader.current) {
|
||||||
loader.current.resetloadMoreItemsCache(false);
|
loader.current.resetloadMoreItemsCache(false);
|
||||||
setItemData(() => []);
|
setItemData([]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollTo: (index: number) => {
|
scrollTo: (index: number) => {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
useAlbumTablePagination,
|
useAlbumTablePagination,
|
||||||
useSetAlbumTable,
|
useSetAlbumTable,
|
||||||
useSetAlbumTablePagination,
|
useSetAlbumTablePagination,
|
||||||
|
useAlbumListItemData,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import {
|
import {
|
||||||
|
@ -57,6 +58,8 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) =
|
||||||
const setPage = useSetAlbumStore();
|
const setPage = useSetAlbumStore();
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
|
|
||||||
|
const { itemData, setItemData } = useAlbumListItemData();
|
||||||
|
|
||||||
const pagination = useAlbumTablePagination();
|
const pagination = useAlbumTablePagination();
|
||||||
const setPagination = useSetAlbumTablePagination();
|
const setPagination = useSetAlbumTablePagination();
|
||||||
const setTable = useSetAlbumTable();
|
const setTable = useSetAlbumTable();
|
||||||
|
@ -319,6 +322,7 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) =
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={page?.grid.scrollOffset || 0}
|
initialScrollOffset={page?.grid.scrollOffset || 0}
|
||||||
itemCount={checkAlbumList?.data?.totalRecordCount || 0}
|
itemCount={checkAlbumList?.data?.totalRecordCount || 0}
|
||||||
|
itemData={itemData}
|
||||||
itemGap={20}
|
itemGap={20}
|
||||||
itemSize={150 + page.grid?.size}
|
itemSize={150 + page.grid?.size}
|
||||||
itemType={LibraryItem.ALBUM}
|
itemType={LibraryItem.ALBUM}
|
||||||
|
@ -327,6 +331,7 @@ export const AlbumListContent = ({ gridRef, tableRef }: AlbumListContentProps) =
|
||||||
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
|
route: AppRoute.LIBRARY_ALBUMS_DETAIL,
|
||||||
slugs: [{ idProperty: 'id', slugProperty: 'albumId' }],
|
slugs: [{ idProperty: 'id', slugProperty: 'albumId' }],
|
||||||
}}
|
}}
|
||||||
|
setItemData={setItemData}
|
||||||
width={width}
|
width={width}
|
||||||
onScroll={handleGridScroll}
|
onScroll={handleGridScroll}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
useSetAlbumArtistStore,
|
useSetAlbumArtistStore,
|
||||||
useSetAlbumArtistTable,
|
useSetAlbumArtistTable,
|
||||||
useSetAlbumArtistTablePagination,
|
useSetAlbumArtistTablePagination,
|
||||||
|
useAlbumArtistListItemData,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
import type { AgGridReact as AgGridReactType } from '@ag-grid-community/react/lib/agGridReact';
|
||||||
import {
|
import {
|
||||||
|
@ -56,6 +57,8 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon
|
||||||
const setPage = useSetAlbumArtistStore();
|
const setPage = useSetAlbumArtistStore();
|
||||||
const handlePlayQueueAdd = usePlayQueueAdd();
|
const handlePlayQueueAdd = usePlayQueueAdd();
|
||||||
|
|
||||||
|
const { itemData, setItemData } = useAlbumArtistListItemData();
|
||||||
|
|
||||||
const pagination = useAlbumArtistTablePagination();
|
const pagination = useAlbumArtistTablePagination();
|
||||||
const setPagination = useSetAlbumArtistTablePagination();
|
const setPagination = useSetAlbumArtistTablePagination();
|
||||||
const setTable = useSetAlbumArtistTable();
|
const setTable = useSetAlbumArtistTable();
|
||||||
|
@ -120,6 +123,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon
|
||||||
},
|
},
|
||||||
rowCount: undefined,
|
rowCount: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
params.api.setDatasource(dataSource);
|
params.api.setDatasource(dataSource);
|
||||||
params.api.ensureIndexVisible(page.table.scrollOffset || 0, 'top');
|
params.api.ensureIndexVisible(page.table.scrollOffset || 0, 'top');
|
||||||
},
|
},
|
||||||
|
@ -298,6 +302,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon
|
||||||
height={height}
|
height={height}
|
||||||
initialScrollOffset={page?.grid.scrollOffset || 0}
|
initialScrollOffset={page?.grid.scrollOffset || 0}
|
||||||
itemCount={checkAlbumArtistList?.data?.totalRecordCount || 0}
|
itemCount={checkAlbumArtistList?.data?.totalRecordCount || 0}
|
||||||
|
itemData={itemData}
|
||||||
itemGap={20}
|
itemGap={20}
|
||||||
itemSize={150 + page.grid?.size}
|
itemSize={150 + page.grid?.size}
|
||||||
itemType={LibraryItem.ALBUM_ARTIST}
|
itemType={LibraryItem.ALBUM_ARTIST}
|
||||||
|
@ -306,6 +311,7 @@ export const AlbumArtistListContent = ({ gridRef, tableRef }: AlbumArtistListCon
|
||||||
route: AppRoute.LIBRARY_ALBUMARTISTS_DETAIL,
|
route: AppRoute.LIBRARY_ALBUMARTISTS_DETAIL,
|
||||||
slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }],
|
slugs: [{ idProperty: 'id', slugProperty: 'albumArtistId' }],
|
||||||
}}
|
}}
|
||||||
|
setItemData={setItemData}
|
||||||
width={width}
|
width={width}
|
||||||
onScroll={handleGridScroll}
|
onScroll={handleGridScroll}
|
||||||
/>
|
/>
|
||||||
|
|
37
src/renderer/store/album-artist-list-data.store.ts
Normal file
37
src/renderer/store/album-artist-list-data.store.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import create from 'zustand';
|
||||||
|
import { devtools } from 'zustand/middleware';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
export interface AlbumArtistListDataState {
|
||||||
|
itemData: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AlbumArtistListDataSlice extends AlbumArtistListDataState {
|
||||||
|
actions: {
|
||||||
|
setItemData: (data: any[]) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAlbumArtistListDataStore = create<AlbumArtistListDataSlice>()(
|
||||||
|
devtools(
|
||||||
|
immer((set) => ({
|
||||||
|
actions: {
|
||||||
|
setItemData: (data) => {
|
||||||
|
set((state) => {
|
||||||
|
state.itemData = data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
itemData: [],
|
||||||
|
})),
|
||||||
|
{ name: 'store_album_list_data' },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useAlbumArtistListStoreActions = () =>
|
||||||
|
useAlbumArtistListDataStore((state) => state.actions);
|
||||||
|
|
||||||
|
export const useAlbumArtistListItemData = () =>
|
||||||
|
useAlbumArtistListDataStore((state) => {
|
||||||
|
return { itemData: state.itemData, setItemData: state.actions.setItemData };
|
||||||
|
});
|
36
src/renderer/store/album-list-data.store.ts
Normal file
36
src/renderer/store/album-list-data.store.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import create from 'zustand';
|
||||||
|
import { devtools } from 'zustand/middleware';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
export interface AlbumListDataState {
|
||||||
|
itemData: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AlbumListDataSlice extends AlbumListDataState {
|
||||||
|
actions: {
|
||||||
|
setItemData: (data: any[]) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAlbumListDataStore = create<AlbumListDataSlice>()(
|
||||||
|
devtools(
|
||||||
|
immer((set) => ({
|
||||||
|
actions: {
|
||||||
|
setItemData: (data) => {
|
||||||
|
set((state) => {
|
||||||
|
state.itemData = data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
itemData: [],
|
||||||
|
})),
|
||||||
|
{ name: 'store_album_list_data' },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useAlbumListStoreActions = () => useAlbumListDataStore((state) => state.actions);
|
||||||
|
|
||||||
|
export const useAlbumListItemData = () =>
|
||||||
|
useAlbumListDataStore((state) => {
|
||||||
|
return { itemData: state.itemData, setItemData: state.actions.setItemData };
|
||||||
|
});
|
|
@ -5,3 +5,5 @@ export * from './album.store';
|
||||||
export * from './song.store';
|
export * from './song.store';
|
||||||
export * from './album-artist.store';
|
export * from './album-artist.store';
|
||||||
export * from './playlist.store';
|
export * from './playlist.store';
|
||||||
|
export * from './album-list-data.store';
|
||||||
|
export * from './album-artist-list-data.store';
|
||||||
|
|
Reference in a new issue