Rearrange docs
This commit is contained in:
227
docs/validation-table-scroll-issue.md
Normal file
227
docs/validation-table-scroll-issue.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# ValidationTable Scroll Position Issue
|
||||
|
||||
## Problem Description
|
||||
|
||||
The `ValidationTable` component in the inventory application suffers from a persistent scroll position issue. When the table content updates or re-renders, the scroll position resets to the top left corner. This creates a poor user experience, especially when users are working with large datasets and need to maintain their position while making edits or filtering data.
|
||||
|
||||
Specific behaviors:
|
||||
- Scroll position resets to the top left corner during re-renders
|
||||
- User loses their place in the table when data is updated
|
||||
- The table does not preserve vertical or horizontal scroll position
|
||||
|
||||
## Relevant Files
|
||||
|
||||
- **`inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx`**
|
||||
- Main component that renders the validation table
|
||||
- Handles scroll position management
|
||||
|
||||
- **`inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx`**
|
||||
- Parent component that wraps ValidationTable
|
||||
- Creates an EnhancedValidationTable wrapper component
|
||||
- Manages data and state for the validation table
|
||||
|
||||
- **`inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx`**
|
||||
- Provides state management and data manipulation functions
|
||||
- Contains scroll-related code in the `updateRow` function
|
||||
|
||||
- **`inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationCell.tsx`**
|
||||
- Renders individual cells in the table
|
||||
- May influence re-renders that affect scroll position
|
||||
|
||||
## Failed Attempts
|
||||
|
||||
We've tried multiple approaches to fix the scroll position issue, none of which have been successful:
|
||||
|
||||
### 1. Using Refs for Scroll Position
|
||||
|
||||
```typescript
|
||||
const scrollPosition = useRef({
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
|
||||
// Capture position on scroll
|
||||
const handleScroll = useCallback(() => {
|
||||
if (tableContainerRef.current) {
|
||||
scrollPosition.current = {
|
||||
left: tableContainerRef.current.scrollLeft,
|
||||
top: tableContainerRef.current.scrollTop
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Restore in useLayoutEffect
|
||||
useLayoutEffect(() => {
|
||||
const container = tableContainerRef.current;
|
||||
if (container) {
|
||||
const { left, top } = scrollPosition.current;
|
||||
if (left || top) {
|
||||
container.scrollLeft = left;
|
||||
container.scrollTop = top;
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Result: Scroll position was still lost during updates.
|
||||
|
||||
### 2. Multiple Restoration Attempts with Timeouts
|
||||
|
||||
```typescript
|
||||
// Multiple timeouts at different intervals
|
||||
setTimeout(() => {
|
||||
if (tableContainerRef.current) {
|
||||
tableContainerRef.current.scrollTop = savedPosition.top;
|
||||
tableContainerRef.current.scrollLeft = savedPosition.left;
|
||||
}
|
||||
}, 0);
|
||||
|
||||
setTimeout(() => {
|
||||
if (tableContainerRef.current) {
|
||||
tableContainerRef.current.scrollTop = savedPosition.top;
|
||||
tableContainerRef.current.scrollLeft = savedPosition.left;
|
||||
}
|
||||
}, 50);
|
||||
|
||||
// Additional timeouts at 100ms, 300ms
|
||||
```
|
||||
|
||||
Result: Still not reliable, scroll position would reset between timeouts or after all timeouts completed.
|
||||
|
||||
### 3. Using MutationObserver and ResizeObserver
|
||||
|
||||
```typescript
|
||||
// Create a mutation observer to detect DOM changes
|
||||
const mutationObserver = new MutationObserver(() => {
|
||||
if (shouldPreserveScroll) {
|
||||
restoreScrollPosition();
|
||||
}
|
||||
});
|
||||
|
||||
// Start observing the table for DOM changes
|
||||
mutationObserver.observe(scrollableContainer, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['style', 'class']
|
||||
});
|
||||
|
||||
// Create a resize observer
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (shouldPreserveScroll) {
|
||||
restoreScrollPosition();
|
||||
}
|
||||
});
|
||||
|
||||
// Observe the table container
|
||||
resizeObserver.observe(scrollableContainer);
|
||||
```
|
||||
|
||||
Result: Did not reliably maintain scroll position, and sometimes caused other rendering issues.
|
||||
|
||||
### 4. Recursive Restoration Approach
|
||||
|
||||
```typescript
|
||||
let attempts = 0;
|
||||
const maxAttempts = 5;
|
||||
|
||||
const restore = () => {
|
||||
if (tableContainerRef.current) {
|
||||
tableContainerRef.current.scrollTop = y;
|
||||
tableContainerRef.current.scrollLeft = x;
|
||||
|
||||
attempts++;
|
||||
if (attempts < maxAttempts) {
|
||||
setTimeout(restore, 50 * attempts);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
restore();
|
||||
```
|
||||
|
||||
Result: No improvement, scroll position still reset.
|
||||
|
||||
### 5. Using React State for Scroll Position
|
||||
|
||||
```typescript
|
||||
const [scrollPos, setScrollPos] = useState<{top: number; left: number}>({top: 0, left: 0});
|
||||
|
||||
// Track the scroll event
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (scrollContainerRef.current) {
|
||||
setScrollPos({
|
||||
top: scrollContainerRef.current.scrollTop,
|
||||
left: scrollContainerRef.current.scrollLeft
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Add scroll listener...
|
||||
}, []);
|
||||
|
||||
// Restore scroll position
|
||||
useLayoutEffect(() => {
|
||||
const container = scrollContainerRef.current;
|
||||
const { top, left } = scrollPos;
|
||||
|
||||
if (top > 0 || left > 0) {
|
||||
requestAnimationFrame(() => {
|
||||
if (container) {
|
||||
container.scrollTop = top;
|
||||
container.scrollLeft = left;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [scrollPos, data]);
|
||||
```
|
||||
|
||||
Result: Caused the screen to shake violently when scrolling and did not preserve position.
|
||||
|
||||
### 6. Using Key Attribute for Stability
|
||||
|
||||
```typescript
|
||||
return (
|
||||
<div
|
||||
key="validation-table-container"
|
||||
ref={scrollContainerRef}
|
||||
className="overflow-auto max-h-[calc(100vh-300px)]"
|
||||
>
|
||||
{/* Table content */}
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
Result: Did not resolve the issue and may have contributed to rendering instability.
|
||||
|
||||
### 7. Removing Scroll Management from Other Components
|
||||
|
||||
We removed scroll position management code from:
|
||||
- `useValidationState.tsx` (in the updateRow function)
|
||||
- `ValidationContainer.tsx` (in the enhancedUpdateRow function)
|
||||
|
||||
Result: This did not fix the issue either.
|
||||
|
||||
## Current Understanding
|
||||
|
||||
The scroll position issue appears to be complex and likely stems from multiple factors:
|
||||
|
||||
1. React's virtual DOM reconciliation may be replacing the scroll container element during updates
|
||||
2. The table uses complex memo patterns with custom equality checks that may not be working as expected
|
||||
3. The data structure may be changing in ways that cause complete re-renders
|
||||
4. The component hierarchy (with EnhancedValidationTable wrapper) may be affecting DOM stability
|
||||
|
||||
## Next Steps to Consider
|
||||
|
||||
Potential approaches that haven't been tried yet:
|
||||
|
||||
1. Implement a completely separate scroll container that exists outside of React's rendering cycle
|
||||
2. Use a third-party virtualized table library that handles scroll position natively
|
||||
3. Restructure the component hierarchy to minimize re-renders
|
||||
4. Use the React DevTools profiler to identify which components are causing re-renders
|
||||
5. Consider simplifying the data structure to reduce the complexity of renders
|
||||
|
||||
## Conclusion
|
||||
|
||||
This issue has proven particularly challenging to resolve. The current ValidationTable implementation struggles with scroll position preservation despite multiple different approaches. A more fundamental restructuring of the component or its rendering approach may be necessary.
|
||||
Reference in New Issue
Block a user