From 18ba5dbff0b8cc382aad3f293b0de8967561a74e Mon Sep 17 00:00:00 2001 From: inokawa <48897392+inokawa@users.noreply.github.com> Date: Fri, 28 Jul 2023 01:42:29 +0900 Subject: [PATCH] Select scrolling state from store --- src/core/scroller.ts | 3 +-- src/core/store.ts | 47 +++++++++++++++++++++------------------- src/react/VGrid.tsx | 11 ++++------ src/react/VList.tsx | 15 ++++++------- src/react/WVList.tsx | 15 ++++++------- src/react/useSelector.ts | 3 +++ 6 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/core/scroller.ts b/src/core/scroller.ts index e6e87fe5c..2c4f69603 100644 --- a/src/core/scroller.ts +++ b/src/core/scroller.ts @@ -4,7 +4,6 @@ import { ACTION_BEFORE_MANUAL_SCROLL, ScrollJump, VirtualStore, - SCROLL_IDLE, ACTION_SCROLL_END, UPDATE_SIZE, ACTION_MANUAL_SCROLL, @@ -19,7 +18,7 @@ const createOnWheel = ( onScrollStopped: () => void ) => { return throttle((e: WheelEvent) => { - if (store._getScrollDirection() === SCROLL_IDLE) { + if (!store._getIsScrolling()) { // Scroll start should be detected with scroll event return; } diff --git a/src/core/store.ts b/src/core/store.ts index 5c8172cb9..49a47ccc0 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -13,7 +13,7 @@ import { updateCache, } from "./cache"; import type { CacheSnapshot, Writeable } from "./types"; -import { abs, exists, max, min } from "./utils"; +import { abs, max, min } from "./utils"; type ItemJump = Readonly<[sizeDiff: number, index: number]>; export type ScrollJump = Readonly; @@ -56,10 +56,11 @@ type Actions = type Subscriber = (sync?: boolean) => void; -export const UPDATE_SCROLL = 0b0001; -export const UPDATE_SIZE = 0b0010; -export const UPDATE_JUMP = 0b0100; -export const UPDATE_SCROLL_WITH_EVENT = 0b1000; +export const UPDATE_SCROLL = 0b00001; +export const UPDATE_SIZE = 0b00010; +export const UPDATE_JUMP = 0b00100; +export const UPDATE_IS_SCROLLING = 0b01000; +export const UPDATE_SCROLL_WITH_EVENT = 0b10000; export type VirtualStore = { _getCache(): CacheSnapshot; @@ -79,7 +80,7 @@ export type VirtualStore = { _getItemIndexForScrollTo(offset: number): number; _subscribe(target: number, cb: Subscriber): () => void; _update(...action: Actions): void; - _getScrollDirection(): ScrollDirection; + _getIsScrolling(): boolean; _updateCacheLength(length: number): void; }; @@ -88,7 +89,6 @@ export const createVirtualStore = ( itemSize: number | undefined, initialItemCount: number = 0, isReverse: boolean, - onScrollStateChange: (scrolling: boolean) => void, cacheSnapshot?: CacheSnapshot ): VirtualStore => { const shouldAutoEstimateItemSize = !itemSize; @@ -115,15 +115,16 @@ export const createVirtualStore = ( _resized = false; return prev; }; - const updateScrollDirection = (dir: ScrollDirection): boolean | undefined => { + const updateScrollDirection = (dir: ScrollDirection): boolean => { const prev = _scrollDirection; _scrollDirection = dir; - if (_scrollDirection === SCROLL_IDLE) { - return false; - } else if (prev === SCROLL_IDLE) { + if ( + _scrollDirection !== prev && + (_scrollDirection === SCROLL_IDLE || prev === SCROLL_IDLE) + ) { return true; } - return; + return false; }; return { @@ -202,7 +203,6 @@ export const createVirtualStore = ( }, _update(type, payload): void { let shouldSync: boolean | undefined; - let updatedScrollState: boolean | undefined; let mutated = 0; switch (type) { @@ -291,9 +291,13 @@ export const createVirtualStore = ( // Ignore until manual scrolling !_isManualScrolling ) { - updatedScrollState = updateScrollDirection( - scrollOffset > payload ? SCROLL_UP : SCROLL_DOWN - ); + if ( + updateScrollDirection( + scrollOffset > payload ? SCROLL_UP : SCROLL_DOWN + ) + ) { + mutated += UPDATE_IS_SCROLLING; + } } // Ignore manual scroll because it may be called in useEffect/useLayoutEffect and cause the warn below. @@ -311,7 +315,9 @@ export const createVirtualStore = ( break; } case ACTION_SCROLL_END: { - updatedScrollState = updateScrollDirection(SCROLL_IDLE); + if (updateScrollDirection(SCROLL_IDLE)) { + mutated = UPDATE_IS_SCROLLING; + } _isManualScrolling = false; break; } @@ -330,12 +336,9 @@ export const createVirtualStore = ( cb(shouldSync); }); } - if (exists(updatedScrollState)) { - onScrollStateChange(updatedScrollState); - } }, - _getScrollDirection() { - return _scrollDirection; + _getIsScrolling() { + return _scrollDirection !== SCROLL_IDLE; }, _updateCacheLength(length) { // It's ok to be updated in render because states should be calculated consistently regardless cache length diff --git a/src/react/VGrid.tsx b/src/react/VGrid.tsx index 4b08adfa8..f16285ac6 100644 --- a/src/react/VGrid.tsx +++ b/src/react/VGrid.tsx @@ -6,7 +6,6 @@ import { ReactElement, forwardRef, ReactNode, - useState, useImperativeHandle, } from "react"; import { VirtualStore, createVirtualStore } from "../core/store"; @@ -248,8 +247,6 @@ export const VGrid = forwardRef( }, ref ): ReactElement => { - const [verticalScrolling, setVerticalScrolling] = useState(false); - const [horizontalScrolling, setHorizontalScrolling] = useState(false); const [vStore, hStore, resizer, vScroller, hScroller, isRtl] = useStatic( () => { const _isRtl = !!rtlProp; @@ -257,15 +254,13 @@ export const VGrid = forwardRef( rowCount, cellHeight, initialRowCount, - false, - setVerticalScrolling + false ); const _hs = createVirtualStore( colCount, cellWidth, initialColCount, - false, - setHorizontalScrolling + false ); return [ _vs, @@ -283,6 +278,8 @@ export const VGrid = forwardRef( const [startRowIndex, endRowIndex] = useSelector(vStore, vStore._getRange); const [startColIndex, endColIndex] = useSelector(hStore, hStore._getRange); + const verticalScrolling = useSelector(vStore, vStore._getIsScrolling); + const horizontalScrolling = useSelector(hStore, hStore._getIsScrolling); const vJumpCount = useSelector(vStore, vStore._getJumpCount); const hJumpCount = useSelector(hStore, hStore._getJumpCount); const height = useSelector(vStore, vStore._getCorrectedScrollSize, true); diff --git a/src/react/VList.tsx b/src/react/VList.tsx index 40b5cd4b4..7c130be09 100644 --- a/src/react/VList.tsx +++ b/src/react/VList.tsx @@ -6,7 +6,6 @@ import { useImperativeHandle, ReactNode, useEffect, - useState, } from "react"; import { UPDATE_SCROLL_WITH_EVENT, createVirtualStore } from "../core/store"; import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"; @@ -175,7 +174,6 @@ export const VList = forwardRef( const onScroll = useRefWithUpdate(onScrollProp); const onScrollStop = useRefWithUpdate(onScrollStopProp); - const [scrolling, setScrolling] = useState(false); const [store, resizer, scroller, isHorizontal, isRtl] = useStatic(() => { const _isHorizontal = !!horizontalProp; const _isRtl = mode === "rtl"; @@ -184,12 +182,6 @@ export const VList = forwardRef( initialItemSize, initialItemCount, mode === "reverse", - (isScrolling) => { - setScrolling(isScrolling); - if (!isScrolling) { - onScrollStop[refKey] && onScrollStop[refKey](); - } - }, cache ); _store._subscribe(UPDATE_SCROLL_WITH_EVENT, () => { @@ -208,6 +200,7 @@ export const VList = forwardRef( store._updateCacheLength(count); const [startIndex, endIndex] = useSelector(store, store._getRange); + const scrolling = useSelector(store, store._getIsScrolling); const jumpCount = useSelector(store, store._getJumpCount); const scrollSize = useSelector(store, store._getCorrectedScrollSize, true); const rootRef = useRef(null); @@ -229,6 +222,12 @@ export const VList = forwardRef( scroller._fixScrollJump(jump); }, [jumpCount]); + useEffect(() => { + if (!scrolling) { + onScrollStop[refKey] && onScrollStop[refKey](); + } + }, [scrolling]); + useEffect(() => { if (!onRangeChangeProp) return; diff --git a/src/react/WVList.tsx b/src/react/WVList.tsx index d5d703fcc..45887dde0 100644 --- a/src/react/WVList.tsx +++ b/src/react/WVList.tsx @@ -4,7 +4,6 @@ import { ReactElement, ReactNode, useEffect, - useState, forwardRef, useImperativeHandle, } from "react"; @@ -130,7 +129,6 @@ export const WVList = forwardRef( const onScrollStop = useRefWithUpdate(onScrollStopProp); - const [scrolling, setScrolling] = useState(false); const [store, resizer, scroller, isHorizontal] = useStatic(() => { const _isHorizontal = !!horizontalProp; const _store = createVirtualStore( @@ -138,12 +136,6 @@ export const WVList = forwardRef( initialItemSize, initialItemCount, false, - (isScrolling) => { - setScrolling(isScrolling); - if (!isScrolling) { - onScrollStop[refKey] && onScrollStop[refKey](); - } - }, cache ); @@ -158,6 +150,7 @@ export const WVList = forwardRef( store._updateCacheLength(count); const [startIndex, endIndex] = useSelector(store, store._getRange); + const scrolling = useSelector(store, store._getIsScrolling); const jumpCount = useSelector(store, store._getJumpCount); const scrollSize = useSelector(store, store._getCorrectedScrollSize, true); const rootRef = useRef(null); @@ -179,6 +172,12 @@ export const WVList = forwardRef( scroller._fixScrollJump(jump); }, [jumpCount]); + useEffect(() => { + if (!scrolling) { + onScrollStop[refKey] && onScrollStop[refKey](); + } + }, [scrolling]); + useEffect(() => { if (!onRangeChangeProp) return; diff --git a/src/react/useSelector.ts b/src/react/useSelector.ts index 9d9226441..4b010b3ed 100644 --- a/src/react/useSelector.ts +++ b/src/react/useSelector.ts @@ -3,6 +3,7 @@ import { flushSync } from "react-dom"; import { useRefWithUpdate } from "./useRefWithUpdate"; import { refKey } from "./utils"; import { + UPDATE_IS_SCROLLING, UPDATE_JUMP, UPDATE_SCROLL, UPDATE_SIZE, @@ -25,6 +26,8 @@ export const useSelector = ( target = UPDATE_SIZE; } else if (getSnapShot === store._getJumpCount) { target = UPDATE_JUMP; + } else if (getSnapShot === store._getIsScrolling) { + target = UPDATE_IS_SCROLLING; } else { // Others will be item subscribers target = UPDATE_SIZE;