Skip to content

Commit

Permalink
Merge bfccaf1 into cb8d647
Browse files Browse the repository at this point in the history
  • Loading branch information
zombieJ committed Aug 18, 2023
2 parents cb8d647 + bfccaf1 commit 77806ed
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 41 deletions.
8 changes: 3 additions & 5 deletions examples/horizontal-scroll.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const MyItem: React.ForwardRefRenderFunction<
const ForwardMyItem = React.forwardRef(MyItem);

const data: Item[] = [];
for (let i = 0; i < 1000; i += 1) {
for (let i = 0; i < 10000; i += 1) {
data.push({
id: `id_${i}`,
height: 30 + Math.random() * 10,
Expand Down Expand Up @@ -105,17 +105,15 @@ const Demo = () => {
boxSizing: 'border-box',
}}
extraRender={(info) => {
const { offsetX, offsetY, rtl: isRTL } = info;
const sizeInfo = info.getSize('id_3', 'id_5');
const { offsetY, rtl: isRTL } = info;
const sizeInfo = info.getSize('id_5', 'id_10');

return (
<div
style={{
position: 'absolute',
// top: -offsetY,
top: -offsetY + sizeInfo.top,
height: sizeInfo.bottom - sizeInfo.top,
// [isRTL ? 'right' : 'left']: 100 - offsetX,
[isRTL ? 'right' : 'left']: 100,
background: 'rgba(255,0,0,0.9)',
zIndex: 1,
Expand Down
43 changes: 10 additions & 33 deletions src/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useOriginScroll from './hooks/useOriginScroll';
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import { getSpinSize } from './utils/scrollbarUtil';
import { useEvent } from 'rc-util';
import { useGetSize } from './hooks/useGetSize';

const EMPTY_DATA = [];

Expand Down Expand Up @@ -261,14 +262,14 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
const verticalScrollBarRef = useRef<ScrollBarRef>();
const horizontalScrollBarRef = useRef<ScrollBarRef>();

const horizontalScrollBarSpinSize = React.useMemo(() => getSpinSize(size.width, scrollWidth), [
size.width,
scrollWidth,
]);
const verticalScrollBarSpinSize = React.useMemo(() => getSpinSize(size.height, scrollHeight), [
size.height,
scrollHeight,
]);
const horizontalScrollBarSpinSize = React.useMemo(
() => getSpinSize(size.width, scrollWidth),
[size.width, scrollWidth],
);
const verticalScrollBarSpinSize = React.useMemo(
() => getSpinSize(size.height, scrollHeight),
[size.height, scrollHeight],
);

// =============================== In Range ===============================
const maxScrollHeight = scrollHeight - height;
Expand Down Expand Up @@ -456,31 +457,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
}, [start, end, mergedData]);

// ================================ Extra =================================
const getSize = (startKey: React.Key, endKey = startKey) => {
let top = 0;
let bottom = 0;
let total = 0;

const dataLen = mergedData.length;
for (let i = 0; i < dataLen; i += 1) {
const item = mergedData[i];
const key = getKey(item);

const cacheHeight = heights.get(key) ?? itemHeight;
bottom = total + cacheHeight;

if (key === startKey) {
top = total;
}
if (key === endKey) {
break;
}

total = bottom;
}

return { top, bottom };
};
const getSize = useGetSize(mergedData, getKey, heights, itemHeight);

const extraContent = extraRender?.({
start,
Expand Down
53 changes: 53 additions & 0 deletions src/hooks/useGetSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type CacheMap from '../utils/CacheMap';
import type { GetKey, GetSize } from '../interface';
import * as React from 'react';

/**
* Size info need loop query for the `heights` which will has the perf issue.
* Let cache result for each render phase.
*/
export function useGetSize<T>(
mergedData: T[],
getKey: GetKey<T>,
heights: CacheMap,
itemHeight: number,
) {
const [key2Index, bottomList] = React.useMemo<
[key2Index: Map<React.Key, number>, bottomList: number[]]
>(() => [new Map(), []], [mergedData, heights.id, itemHeight]);

const getSize: GetSize = (startKey, endKey = startKey) => {
// Get from cache first
let startIndex = key2Index.get(startKey);
let endIndex = key2Index.get(endKey);

// Loop to fill the cache
if (startIndex === undefined || endIndex === undefined) {
const dataLen = mergedData.length;
for (let i = bottomList.length; i < dataLen; i += 1) {
const item = mergedData[i];
const key = getKey(item);
key2Index.set(key, i);
const cacheHeight = heights.get(key) ?? itemHeight;
bottomList[i] = (bottomList[i - 1] || 0) + cacheHeight;
if (key === startKey) {
startIndex = i;
}
if (key === endKey) {
endIndex = i;
}

if (startIndex !== undefined && endIndex !== undefined) {
break;
}
}
}

return {
top: bottomList[startIndex - 1] || 0,
bottom: bottomList[endIndex],
};
};

return getSize;
}
4 changes: 3 additions & 1 deletion src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface SharedConfig<T> {

export type GetKey<T> = (item: T) => React.Key;

export type GetSize = (startKey: React.Key, endKey?: React.Key) => { top: number; bottom: number };

export interface ExtraRenderInfo {
/** Virtual list start line */
start: number;
Expand All @@ -23,5 +25,5 @@ export interface ExtraRenderInfo {

rtl: boolean;

getSize: (startKey: React.Key, endKey?: React.Key) => { top: number; bottom: number };
getSize: GetSize;
}
9 changes: 7 additions & 2 deletions src/utils/CacheMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ import type React from 'react';
class CacheMap {
maps: Record<string, number>;

// Used for cache key
// `useMemo` no need to update if `id` not change
id: number = 0;

constructor() {
this.maps = Object.create(null);
}

set(key: React.ReactText, value: number) {
set(key: React.Key, value: number) {
this.maps[key] = value;
this.id += 1;
}

get(key: React.ReactText) {
get(key: React.Key) {
return this.maps[key];
}
}
Expand Down

0 comments on commit 77806ed

Please sign in to comment.