diff --git a/src/index.js b/src/index.js index 3b7ab61a..36b93b10 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,8 @@ import React from 'react' import useRect from './useRect' import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect' +export { useRect } + const defaultEstimateSize = () => 50 const defaultKeyExtractor = index => index @@ -84,7 +86,10 @@ export const useElementScroll = ({ } } -const useWindowRect = (windowRef, initialRect = { width: 0, height: 0 }) => { +export const useWindowRect = ( + windowRef, + initialRect = { width: 0, height: 0 } +) => { const [rect, setRect] = React.useState(initialRect) const [element, setElement] = React.useState(windowRef.current) @@ -129,9 +134,8 @@ export const useWindowScroll = ({ const [scrollOffset, setScrollOffset] = React.useState(0) const [element, setElement] = React.useState(windowRef.current) - const parentOffsetRef = React.useRef(0) - const rectKey = horizontal ? 'left' : 'top' + const scrollKey = horizontal ? 'scrollX' : 'scrollY' useIsomorphicLayoutEffect(() => { @@ -140,20 +144,15 @@ export const useWindowScroll = ({ useIsomorphicLayoutEffect(() => { if (!element) { - parentOffsetRef.current = 0 setScrollOffset(0) return } - if (parentRef.current) { - parentOffsetRef.current = - element[scrollKey] + parentRef.current.getBoundingClientRect()[rectKey] - } - const onScroll = () => { - const offset = element[scrollKey] - parentOffsetRef.current - setScrollOffset(offset) + setScrollOffset( + Math.max(0, parentRef.current.getBoundingClientRect()[rectKey] * -1) + ) } onScroll() @@ -166,19 +165,20 @@ export const useWindowScroll = ({ return () => { element.removeEventListener('scroll', onScroll) } - }, [element, scrollKey, rectKey, parentRef]) + }, [element, rectKey, parentRef]) const scrollToFn = React.useCallback( (offset, reason) => { if (windowRef.current) { const delta = ['ToIndex', 'SizeChanged'].includes(reason) - ? parentOffsetRef.current + ? windowRef.current[scrollKey] + + parentRef.current.getBoundingClientRect()[rectKey] : 0 windowRef.current.scrollTo({ [rectKey]: offset + delta }) } }, - [windowRef, rectKey] + [windowRef, parentRef, scrollKey, rectKey] ) const useMeasureParent = useWindowObserver || useWindowRect @@ -304,9 +304,9 @@ export function useVirtual({ start, end, overscan, - size: measurements.length, + size, }), - [start, end, overscan, measurements.length, rangeExtractor] + [start, end, overscan, size, rangeExtractor] ) const measureSizeRef = React.useRef(measureSize) diff --git a/src/tests/index.test.js b/src/tests/index.test.js index 7766e72d..8856015a 100644 --- a/src/tests/index.test.js +++ b/src/tests/index.test.js @@ -172,10 +172,15 @@ describe('useVirtual list', () => { expect(result.current.virtualItems.length).toBe(4) }) it('should handle scroll in window mode', () => { + let offset = 0 + const el = document.createElement('div') + el.getBoundingClientRect = jest.fn(() => { + return { top: offset * -1 } + }) const { result } = renderHook(() => useVirtualImpl({ size: 100, - parentRef: React.useRef(document.createElement('div')), + parentRef: React.useRef(el), windowRef: React.useRef(window), useWindowObserver: () => ({ height: 200, width: '100%' }), overscan: 0, @@ -184,7 +189,8 @@ describe('useVirtual list', () => { ) expect(result.current.virtualItems[0].index).toBe(0) act(() => { - fireEvent.scroll(window, { target: { scrollY: 100 } }) + offset = 100 + fireEvent.scroll(window, { target: { scrollY: offset } }) }) expect(result.current.virtualItems[0].index).toBe(2) }) diff --git a/types/index.d.ts b/types/index.d.ts index df3a8907..ef4b919c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -32,6 +32,16 @@ interface Rect { height: number } +declare function useRect( + ref: React.RefObject, + initialRect?: Rect +): Rect + +declare function useWindowRect( + ref: React.RefObject, + initialRect?: Rect +): Rect + interface ScrollOptions { parentRef: React.RefObject windowRef?: React.RefObject @@ -76,4 +86,10 @@ declare function useVirtual( measure: () => void } -export { defaultRangeExtractor, useVirtual, useDefaultScroll } +export { + defaultRangeExtractor, + useVirtual, + useDefaultScroll, + useRect, + useWindowRect, +}