Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select scrolling state from store #143

Merged
merged 1 commit into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/core/scroller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
ACTION_BEFORE_MANUAL_SCROLL,
ScrollJump,
VirtualStore,
SCROLL_IDLE,
ACTION_SCROLL_END,
UPDATE_SIZE,
ACTION_MANUAL_SCROLL,
Expand All @@ -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;
}
Expand Down
47 changes: 25 additions & 22 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
};

Expand All @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -202,7 +203,6 @@ export const createVirtualStore = (
},
_update(type, payload): void {
let shouldSync: boolean | undefined;
let updatedScrollState: boolean | undefined;
let mutated = 0;

switch (type) {
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
Expand All @@ -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
Expand Down
11 changes: 4 additions & 7 deletions src/react/VGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
ReactElement,
forwardRef,
ReactNode,
useState,
useImperativeHandle,
} from "react";
import { VirtualStore, createVirtualStore } from "../core/store";
Expand Down Expand Up @@ -119,7 +118,7 @@
minWidth: width,
};
return style;
}, [top, left, width, height, vHide, hHide])}

Check warning on line 121 in src/react/VGrid.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has a missing dependency: 'isRtl'. Either include it or remove the dependency array
>
{children}
</Element>
Expand Down Expand Up @@ -248,24 +247,20 @@
},
ref
): ReactElement => {
const [verticalScrolling, setVerticalScrolling] = useState(false);
const [horizontalScrolling, setHorizontalScrolling] = useState(false);
const [vStore, hStore, resizer, vScroller, hScroller, isRtl] = useStatic(
() => {
const _isRtl = !!rtlProp;
const _vs = createVirtualStore(
rowCount,
cellHeight,
initialRowCount,
false,
setVerticalScrolling
false
);
const _hs = createVirtualStore(
colCount,
cellWidth,
initialColCount,
false,
setHorizontalScrolling
false
);
return [
_vs,
Expand All @@ -283,6 +278,8 @@

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);
Expand Down Expand Up @@ -344,7 +341,7 @@
},
};
},
[]

Check warning on line 344 in src/react/VGrid.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useImperativeHandle has missing dependencies: 'hScroller', 'hStore', 'vScroller', and 'vStore'. Either include them or remove the dependency array
);

const render = useMemo(() => {
Expand Down Expand Up @@ -386,7 +383,7 @@
}

return res;
}, [

Check warning on line 386 in src/react/VGrid.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has missing dependencies: 'hStore', 'isRtl', 'itemElement', 'resizer', and 'vStore'. Either include them or remove the dependency array
render,
startRowIndexWithMargin,
endRowIndexWithMargin,
Expand Down Expand Up @@ -416,7 +413,7 @@
...windowAttrs.style,
},
}),
values(windowAttrs)

Check warning on line 416 in src/react/VGrid.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies

Check warning on line 416 in src/react/VGrid.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has a missing dependency: 'windowAttrs'. Either include it or remove the dependency array
)}
>
{items}
Expand Down
15 changes: 7 additions & 8 deletions src/react/VList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
useImperativeHandle,
ReactNode,
useEffect,
useState,
} from "react";
import { UPDATE_SCROLL_WITH_EVENT, createVirtualStore } from "../core/store";
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
Expand Down Expand Up @@ -175,7 +174,6 @@
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";
Expand All @@ -184,12 +182,6 @@
initialItemSize,
initialItemCount,
mode === "reverse",
(isScrolling) => {
setScrolling(isScrolling);
if (!isScrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
},
cache
);
_store._subscribe(UPDATE_SCROLL_WITH_EVENT, () => {
Expand All @@ -208,6 +200,7 @@
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<HTMLDivElement>(null);
Expand All @@ -229,6 +222,12 @@
scroller._fixScrollJump(jump);
}, [jumpCount]);

useEffect(() => {
if (!scrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
}, [scrolling]);

Check warning on line 229 in src/react/VList.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useEffect has a missing dependency: 'onScrollStop'. Either include it or remove the dependency array

useEffect(() => {
if (!onRangeChangeProp) return;

Expand All @@ -237,7 +236,7 @@
end: endIndex,
count,
});
}, [startIndex, endIndex]);

Check warning on line 239 in src/react/VList.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useEffect has missing dependencies: 'count' and 'onRangeChangeProp'. Either include them or remove the dependency array. If 'onRangeChangeProp' changes too often, find the parent component that defines it and wrap that definition in useCallback

useImperativeHandle(
ref,
Expand All @@ -262,7 +261,7 @@
},
};
},
[]

Check warning on line 264 in src/react/VList.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useImperativeHandle has missing dependencies: 'scroller' and 'store'. Either include them or remove the dependency array
);

const startIndexWithMargin = max(startIndex - overscan, 0);
Expand All @@ -288,7 +287,7 @@
}
}
return res;
}, [elements, startIndexWithMargin, endIndexWithMargin]);

Check warning on line 290 in src/react/VList.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook useMemo has missing dependencies: 'isHorizontal', 'isRtl', 'itemElement', 'resizer', and 'store'. Either include them or remove the dependency array

return (
<Window
Expand Down
15 changes: 7 additions & 8 deletions src/react/WVList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
ReactElement,
ReactNode,
useEffect,
useState,
forwardRef,
useImperativeHandle,
} from "react";
Expand Down Expand Up @@ -130,20 +129,13 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(

const onScrollStop = useRefWithUpdate(onScrollStopProp);

const [scrolling, setScrolling] = useState(false);
const [store, resizer, scroller, isHorizontal] = useStatic(() => {
const _isHorizontal = !!horizontalProp;
const _store = createVirtualStore(
count,
initialItemSize,
initialItemCount,
false,
(isScrolling) => {
setScrolling(isScrolling);
if (!isScrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
},
cache
);

Expand All @@ -158,6 +150,7 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
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<HTMLDivElement>(null);
Expand All @@ -179,6 +172,12 @@ export const WVList = forwardRef<WVListHandle, WVListProps>(
scroller._fixScrollJump(jump);
}, [jumpCount]);

useEffect(() => {
if (!scrolling) {
onScrollStop[refKey] && onScrollStop[refKey]();
}
}, [scrolling]);

useEffect(() => {
if (!onRangeChangeProp) return;

Expand Down
3 changes: 3 additions & 0 deletions src/react/useSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -25,6 +26,8 @@ export const useSelector = <T>(
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;
Expand Down
Loading