Fix optimistic rendering issues on views (#2851)
* Fix optimistic rendering issues on views * Remove virtualizer
This commit is contained in:
@@ -1,92 +1,33 @@
|
||||
import { useContext } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import {
|
||||
RecordTableRow,
|
||||
StyledRow,
|
||||
} from '@/ui/object/record-table/components/RecordTableRow';
|
||||
import { RecordTableBodyFetchMoreLoader } from '@/ui/object/record-table/components/RecordTableBodyFetchMoreLoader';
|
||||
import { RecordTableRow } from '@/ui/object/record-table/components/RecordTableRow';
|
||||
import { RowIdContext } from '@/ui/object/record-table/contexts/RowIdContext';
|
||||
import { RowIndexContext } from '@/ui/object/record-table/contexts/RowIndexContext';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
|
||||
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
|
||||
import { getRecordTableScopedStates } from '@/ui/object/record-table/utils/getRecordTableScopedStates';
|
||||
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import RenderIfVisible from '@/ui/utilities/virtualizer/RenderIfVisible';
|
||||
|
||||
export const RecordTableBody = () => {
|
||||
const { scopeId } = useRecordTable();
|
||||
|
||||
const onLastRowVisible = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (inView: boolean) => {
|
||||
const { tableLastRowVisibleState } = getRecordTableScopedStates({
|
||||
recordTableScopeId: scopeId,
|
||||
});
|
||||
|
||||
set(tableLastRowVisibleState, inView);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const { ref: lastTableRowRef } = useInView({
|
||||
onChange: onLastRowVisible,
|
||||
});
|
||||
|
||||
const tableRowIds = useRecoilValue(tableRowIdsState);
|
||||
|
||||
const [isFetchingMoreObjects] = useRecoilState(
|
||||
isFetchingMoreRecordsFamilyState(scopeId),
|
||||
);
|
||||
|
||||
const isFetchingRecordTableData = useRecoilValue(
|
||||
isFetchingRecordTableDataState,
|
||||
);
|
||||
|
||||
const lastRowId = tableRowIds[tableRowIds.length - 1];
|
||||
|
||||
const scrollWrapperRef = useContext(ScrollWrapperContext);
|
||||
|
||||
if (isFetchingRecordTableData) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{tableRowIds.map((rowId, rowIndex) => (
|
||||
{tableRowIds.slice().map((rowId, rowIndex) => (
|
||||
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||
<RowIndexContext.Provider value={rowIndex}>
|
||||
<RenderIfVisible
|
||||
rootElement="tbody"
|
||||
placeholderElement="tr"
|
||||
defaultHeight={32}
|
||||
initialVisible={rowIndex < 30}
|
||||
root={scrollWrapperRef.current}
|
||||
>
|
||||
<RecordTableRow
|
||||
key={rowId}
|
||||
ref={
|
||||
rowId === lastRowId && rowIndex > 30
|
||||
? lastTableRowRef
|
||||
: undefined
|
||||
}
|
||||
rowId={rowId}
|
||||
/>
|
||||
</RenderIfVisible>
|
||||
<RecordTableRow key={rowId} rowId={rowId} />
|
||||
</RowIndexContext.Provider>
|
||||
</RowIdContext.Provider>
|
||||
))}
|
||||
<tbody>
|
||||
{isFetchingMoreObjects && (
|
||||
<StyledRow selected={false}>
|
||||
<td style={{ height: 50 }} colSpan={1000}>
|
||||
Loading more...
|
||||
</td>
|
||||
</StyledRow>
|
||||
)}
|
||||
</tbody>
|
||||
<RecordTableBodyFetchMoreLoader />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const RecordTableBodyEffect = () => {
|
||||
const {
|
||||
fetchMoreRecords: fetchMoreObjects,
|
||||
records,
|
||||
setRecordTableData,
|
||||
queryStateIdentifier,
|
||||
} = useObjectRecordTable();
|
||||
const { tableLastRowVisibleState } = useRecordTableScopedStates();
|
||||
const tableLastRowVisible = useRecoilValue(tableLastRowVisibleState);
|
||||
const [tableLastRowVisible, setTableLastRowVisible] = useRecoilState(
|
||||
tableLastRowVisibleState,
|
||||
);
|
||||
|
||||
const isFetchingMoreObjects = useRecoilValue(
|
||||
isFetchingMoreRecordsFamilyState(queryStateIdentifier),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setRecordTableData(records);
|
||||
}, [records, setRecordTableData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableLastRowVisible && isDefined(fetchMoreObjects)) {
|
||||
if (tableLastRowVisible && !isFetchingMoreObjects) {
|
||||
fetchMoreObjects();
|
||||
}
|
||||
}, [fetchMoreObjects, tableLastRowVisible]);
|
||||
}, [
|
||||
fetchMoreObjects,
|
||||
isFetchingMoreObjects,
|
||||
setTableLastRowVisible,
|
||||
tableLastRowVisible,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordTable } from '@/object-record/hooks/useObjectRecordTable';
|
||||
import { isFetchingMoreRecordsFamilyState } from '@/object-record/states/isFetchingMoreRecordsFamilyState';
|
||||
import { StyledRow } from '@/ui/object/record-table/components/RecordTableRow';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
|
||||
import { getRecordTableScopedStates } from '@/ui/object/record-table/utils/getRecordTableScopedStates';
|
||||
|
||||
export const RecordTableBodyFetchMoreLoader = () => {
|
||||
const { queryStateIdentifier } = useObjectRecordTable();
|
||||
const { scopeId } = useRecordTable();
|
||||
|
||||
const isFetchingMoreObjects = useRecoilValue(
|
||||
isFetchingMoreRecordsFamilyState(queryStateIdentifier),
|
||||
);
|
||||
|
||||
const isFetchingRecordTableData = useRecoilValue(
|
||||
isFetchingRecordTableDataState,
|
||||
);
|
||||
|
||||
const onLastRowVisible = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (inView: boolean) => {
|
||||
const { tableLastRowVisibleState } = getRecordTableScopedStates({
|
||||
recordTableScopeId: scopeId,
|
||||
});
|
||||
set(tableLastRowVisibleState, inView);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const { ref: tbodyRef } = useInView({
|
||||
onChange: onLastRowVisible,
|
||||
});
|
||||
|
||||
if (isFetchingRecordTableData) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tbody ref={tbodyRef}>
|
||||
{isFetchingMoreObjects && (
|
||||
<StyledRow selected={false}>
|
||||
<td style={{ height: 50 }} colSpan={1000}>
|
||||
Loading more...
|
||||
</td>
|
||||
</StyledRow>
|
||||
)}
|
||||
</tbody>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,10 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
import { ColumnContext } from '../contexts/ColumnContext';
|
||||
import { useRecordTableScopedStates } from '../hooks/internal/useRecordTableScopedStates';
|
||||
import { useCurrentRowSelected } from '../record-table-row/hooks/useCurrentRowSelected';
|
||||
@@ -18,36 +21,53 @@ type RecordTableRowProps = {
|
||||
rowId: string;
|
||||
};
|
||||
|
||||
export const RecordTableRow = forwardRef<
|
||||
HTMLTableRowElement,
|
||||
RecordTableRowProps
|
||||
>(({ rowId }, ref) => {
|
||||
const StyledPlaceholder = styled.td`
|
||||
height: 30px;
|
||||
`;
|
||||
|
||||
export const RecordTableRow = ({ rowId }: RecordTableRowProps) => {
|
||||
const { visibleTableColumnsSelector } = useRecordTableScopedStates();
|
||||
|
||||
const visibleTableColumns = useRecoilValue(visibleTableColumnsSelector);
|
||||
|
||||
const { currentRowSelected } = useCurrentRowSelected();
|
||||
|
||||
const scrollWrapperRef = useContext(ScrollWrapperContext);
|
||||
|
||||
const { ref: elementRef, inView } = useInView({
|
||||
root: scrollWrapperRef.current,
|
||||
rootMargin: '1000px',
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledRow
|
||||
ref={ref}
|
||||
ref={elementRef}
|
||||
data-testid={`row-id-${rowId}`}
|
||||
selected={currentRowSelected}
|
||||
data-selectable-id={rowId}
|
||||
>
|
||||
<td>
|
||||
<CheckboxCell />
|
||||
</td>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column, columnIndex) => {
|
||||
return (
|
||||
<ColumnContext.Provider value={column} key={column.fieldMetadataId}>
|
||||
<RecordTableCell cellIndex={columnIndex} />
|
||||
</ColumnContext.Provider>
|
||||
);
|
||||
})}
|
||||
<td></td>
|
||||
{inView ? (
|
||||
<>
|
||||
<td>
|
||||
<CheckboxCell />
|
||||
</td>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column, columnIndex) => {
|
||||
return (
|
||||
<ColumnContext.Provider
|
||||
value={column}
|
||||
key={column.fieldMetadataId}
|
||||
>
|
||||
<RecordTableCell cellIndex={columnIndex} />
|
||||
</ColumnContext.Provider>
|
||||
);
|
||||
})}
|
||||
<td></td>
|
||||
</>
|
||||
) : (
|
||||
<StyledPlaceholder />
|
||||
)}
|
||||
</StyledRow>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user