diff --git a/packages/toolpad-core/src/Crud/Edit.tsx b/packages/toolpad-core/src/Crud/Edit.tsx index 7d16f689aba..02724f2576f 100644 --- a/packages/toolpad-core/src/Crud/Edit.tsx +++ b/packages/toolpad-core/src/Crud/Edit.tsx @@ -249,21 +249,34 @@ function Edit(props: EditProps) { const { fields, validate, ...methods } = cachedDataSource; const { getOne, updateOne } = methods; - const [data, setData] = React.useState(null); - const [isLoading, setIsLoading] = React.useState(false); + const cachedData = React.useMemo( + () => cache && (cache.get(JSON.stringify(['getOne', id])) as D), + [cache, id], + ); + + const [data, setData] = React.useState(cachedData); + const [isLoading, setIsLoading] = React.useState(!cachedData); const [error, setError] = React.useState(null); const loadData = React.useCallback(async () => { setError(null); - setIsLoading(true); - try { - const showData = await getOne(id); + + let showData = cachedData; + if (!showData) { + setIsLoading(true); + + try { + showData = await getOne(id); + } catch (showDataError) { + setError(showDataError as Error); + } + } + + if (showData) { setData(showData); - } catch (showDataError) { - setError(showDataError as Error); } setIsLoading(false); - }, [getOne, id]); + }, [cachedData, getOne, id]); React.useEffect(() => { loadData(); diff --git a/packages/toolpad-core/src/Crud/List.tsx b/packages/toolpad-core/src/Crud/List.tsx index eac1a42c6d3..7451806a714 100644 --- a/packages/toolpad-core/src/Crud/List.tsx +++ b/packages/toolpad-core/src/Crud/List.tsx @@ -1,13 +1,12 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import { styled } from '@mui/material'; +import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; -import Typography from '@mui/material/Typography'; import { DataGrid, GridToolbar, @@ -37,21 +36,6 @@ import { useCachedDataSource } from './useCachedDataSource'; import type { DataModel, DataModelId, DataSource } from './types'; import { CRUD_DEFAULT_LOCALE_TEXT, type CRUDLocaleText } from './localeText'; -const ErrorOverlay = styled('div')(({ theme }) => ({ - position: 'absolute', - backgroundColor: theme.palette.error.light, - borderRadius: '4px', - top: 0, - height: '100%', - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - textAlign: 'center', - p: 1, - zIndex: 10, -})); - export interface ListSlotProps { dataGrid?: Partial; } @@ -162,11 +146,6 @@ function List(props: ListProps) { const dialogs = useDialogs(); const notifications = useNotifications(); - const [rowsState, setRowsState] = React.useState<{ rows: D[]; rowCount: number }>({ - rows: [], - rowCount: 0, - }); - const [paginationModel, setPaginationModel] = React.useState({ page: routerContext?.searchParams.get('page') ? Number(routerContext?.searchParams.get('page')) @@ -186,7 +165,30 @@ function List(props: ListProps) { : [], ); - const [isLoading, setIsLoading] = React.useState(true); + const cachedData = React.useMemo( + () => + cache && + (cache.get( + JSON.stringify([ + 'getMany', + { + paginationModel, + sortModel, + filterModel, + }, + ]), + ) as { + items: D[]; + itemCount: number; + }), + [cache, filterModel, paginationModel, sortModel], + ); + + const [rowsState, setRowsState] = React.useState<{ rows: D[]; rowCount: number }>({ + rows: cachedData?.items ?? [], + rowCount: cachedData?.itemCount ?? 0, + }); + const [isLoading, setIsLoading] = React.useState(!cachedData); const [error, setError] = React.useState(null); const handlePaginationModelChange = React.useCallback( @@ -263,26 +265,34 @@ function List(props: ListProps) { const loadData = React.useCallback(async () => { setError(null); - setIsLoading(true); - try { - const listData = await getMany({ - paginationModel, - sortModel, - filterModel, - }); + + let listData = cachedData; + if (!listData) { + setIsLoading(true); + + try { + listData = await getMany({ + paginationModel, + sortModel, + filterModel, + }); + } catch (listDataError) { + setError(listDataError as Error); + } + } + + if (listData) { setRowsState({ rows: listData.items, rowCount: listData.itemCount, }); - } catch (listDataError) { - setError(listDataError as Error); } setIsLoading(false); - }, [filterModel, getMany, paginationModel, sortModel]); + }, [cachedData, filterModel, getMany, paginationModel, sortModel]); React.useEffect(() => { loadData(); - }, [filterModel, getMany, loadData, paginationModel, sortModel]); + }, [loadData]); const handleRefresh = React.useCallback(() => { if (!isLoading) { @@ -412,69 +422,70 @@ function List(props: ListProps) { return ( - - -
- - - -
-
- {onCreateClick ? ( - - ) : null} -
- - {/* Use NoSsr to prevent issue https://github.com/mui/mui-x/issues/17077 as DataGrid has no SSR support */} - - )} - sx={{ - [`& .${gridClasses.columnHeader}, & .${gridClasses.cell}`]: { - outline: 'transparent', - }, - [`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses.cell}:focus-within`]: - { - outline: 'none', + {error ? ( + + {error.message} + + ) : ( + + + +
+ + + +
+
+ {onCreateClick ? ( + + ) : null} +
+ {/* Use NoSsr to prevent issue https://github.com/mui/mui-x/issues/17077 as DataGrid has no SSR support */} + + )} + sx={{ + [`& .${gridClasses.columnHeader}, & .${gridClasses.cell}`]: { + outline: 'transparent', }, - ...(onRowClick - ? { - [`& .${gridClasses.row}:hover`]: { - cursor: 'pointer', - }, - } - : {}), - ...slotProps?.dataGrid?.sx, - }} - /> - - {error && ( - - {error.message} - - )} -
+ [`& .${gridClasses.columnHeader}:focus-within, & .${gridClasses.cell}:focus-within`]: + { + outline: 'none', + }, + ...(onRowClick + ? { + [`& .${gridClasses.row}:hover`]: { + cursor: 'pointer', + }, + } + : {}), + ...slotProps?.dataGrid?.sx, + }} + /> + + + )}
); } diff --git a/packages/toolpad-core/src/Crud/Show.tsx b/packages/toolpad-core/src/Crud/Show.tsx index 0e8cae05917..5ddbeb13e09 100644 --- a/packages/toolpad-core/src/Crud/Show.tsx +++ b/packages/toolpad-core/src/Crud/Show.tsx @@ -85,23 +85,36 @@ function Show(props: ShowProps) { const dialogs = useDialogs(); const notifications = useNotifications(); - const [data, setData] = React.useState(null); - const [isLoading, setIsLoading] = React.useState(false); + const cachedData = React.useMemo( + () => cache && (cache.get(JSON.stringify(['getOne', id])) as D), + [cache, id], + ); + + const [data, setData] = React.useState(cachedData); + const [isLoading, setIsLoading] = React.useState(!cachedData); const [error, setError] = React.useState(null); const [hasDeleted, setHasDeleted] = React.useState(false); const loadData = React.useCallback(async () => { setError(null); - setIsLoading(true); - try { - const showData = await getOne(id); + + let showData = cachedData; + if (!showData) { + setIsLoading(true); + + try { + showData = await getOne(id); + } catch (showDataError) { + setError(showDataError as Error); + } + } + + if (showData) { setData(showData); - } catch (showDataError) { - setError(showDataError as Error); } setIsLoading(false); - }, [getOne, id]); + }, [cachedData, getOne, id]); React.useEffect(() => { loadData();