Skip to content

Commit 7130ddc

Browse files
committed
feat: add onVisibleChange prop
1 parent 1cf7d65 commit 7130ddc

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

src/List.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useRef, useState } from 'react';
2+
import { useRef, useState, useLayoutEffect } from 'react';
33
import classNames from 'classnames';
44
import Filler from './Filler';
55
import ScrollBar from './ScrollBar';
@@ -50,6 +50,8 @@ export interface ListProps<T> extends React.HTMLAttributes<any> {
5050
virtual?: boolean;
5151

5252
onScroll?: React.UIEventHandler<HTMLElement>;
53+
/** Trigger when render list item changed */
54+
onVisibleChange?: (visibleList: T[], fullList: T[]) => void;
5355
}
5456

5557
export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
@@ -66,6 +68,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
6668
virtual,
6769
component: Component = 'div',
6870
onScroll,
71+
onVisibleChange,
6972
...restProps
7073
} = props;
7174

@@ -99,7 +102,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
99102

100103
// ================================ Scroll ================================
101104
function syncScrollTop(newTop: number | ((prev: number) => number)) {
102-
setScrollTop(origin => {
105+
setScrollTop((origin) => {
103106
let value: number;
104107
if (typeof newTop === 'function') {
105108
value = newTop(origin);
@@ -242,8 +245,8 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
242245
useVirtual,
243246
isScrollAtTop,
244247
isScrollAtBottom,
245-
offsetY => {
246-
syncScrollTop(top => {
248+
(offsetY) => {
249+
syncScrollTop((top) => {
247250
const newTop = top + offsetY;
248251
return newTop;
249252
});
@@ -260,7 +263,7 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
260263
return true;
261264
});
262265

263-
React.useLayoutEffect(() => {
266+
useLayoutEffect(() => {
264267
// Firefox only
265268
function onMozMousePixelScroll(e: Event) {
266269
if (useVirtual) {
@@ -297,6 +300,14 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
297300
scrollTo,
298301
}));
299302

303+
// ================================ Effect ================================
304+
/** We need told outside that some list not rendered */
305+
useLayoutEffect(() => {
306+
const renderList = mergedData.slice(start, end);
307+
308+
onVisibleChange?.(renderList, mergedData);
309+
}, [start, end, mergedData]);
310+
300311
// ================================ Render ================================
301312
const listChildren = useChildren(mergedData, start, end, setInstanceRef, children, sharedConfig);
302313

tests/list.test.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,14 @@ describe('List.Basic', () => {
8181
});
8282

8383
it('scroll it', () => {
84+
const onVisibleChange = jest.fn();
85+
8486
// scroll to top
8587
scrollTop = 0;
86-
const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) });
88+
const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100), onVisibleChange });
8789
expect(wrapper.find(Filler).props().height).toEqual(2000);
8890
expect(wrapper.find(Filler).props().offset).toEqual(0);
91+
onVisibleChange.mockReset();
8992

9093
// scrollTop to end
9194
scrollTop = 2000 - 100;
@@ -94,6 +97,9 @@ describe('List.Basic', () => {
9497
});
9598
expect(wrapper.find(Filler).props().height).toEqual(2000);
9699
expect(wrapper.find(Filler).props().offset + wrapper.find('li').length * 20).toEqual(2000);
100+
101+
expect(onVisibleChange.mock.calls[0][0]).toHaveLength(6);
102+
expect(onVisibleChange.mock.calls[0][1]).toHaveLength(100);
97103
});
98104
});
99105

@@ -191,23 +197,15 @@ describe('List.Basic', () => {
191197

192198
it('work', async () => {
193199
const wrapper = genList({ itemHeight: 20, height: 40, data: genData(3) });
194-
wrapper
195-
.find('Filler')
196-
.find('ResizeObserver')
197-
.props()
198-
.onResize({ offsetHeight: 0 });
200+
wrapper.find('Filler').find('ResizeObserver').props().onResize({ offsetHeight: 0 });
199201
expect(collected).toBeFalsy();
200202

201203
// Wait for collection
202204
await act(async () => {
203205
await Promise.resolve();
204206
});
205207

206-
wrapper
207-
.find('Filler')
208-
.find('ResizeObserver')
209-
.props()
210-
.onResize({ offsetHeight: 100 });
208+
wrapper.find('Filler').find('ResizeObserver').props().onResize({ offsetHeight: 100 });
211209
expect(collected).toBeTruthy();
212210
});
213211
});

0 commit comments

Comments
 (0)