refactor: 新版虚拟列表增加加载更多与触底锁定

pull/81/head
moonrailgun 4 years ago
parent 57737d76a7
commit 3c4d5e14b5

@ -1,39 +1,103 @@
import React, { useRef } from 'react';
import React, { useDebugValue, useMemo, useRef } from 'react';
import { buildMessageItemRow } from './Item';
import type { MessageListProps } from './types';
import { Virtuoso, VirtuosoGridHandle } from 'react-virtuoso';
import {
FollowOutputScalarType,
Virtuoso,
VirtuosoGridHandle,
} from 'react-virtuoso';
import type { ChatMessage } from 'tailchat-shared';
const PREPEND_OFFSET = 10 ** 7;
const virtuosoStyle: React.CSSProperties = {
height: '100%',
};
/**
*
* : https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/VirtualizedMessageList.tsx
*/
export const VirtualizedMessageList: React.FC<MessageListProps> = React.memo(
(props) => {
const listRef = useRef<VirtuosoGridHandle>();
const lastMessageId = useRef('');
const numItemsPrepended = usePrependedMessagesCount(props.messages);
useDebugValue(numItemsPrepended);
const handleLoadMore = () => {
// TODO: 待修复, 这个方法只会被触发一次
lastMessageId.current = props.messages[0]._id;
props.onLoadMore().then(() => {
listRef.current?.scrollToIndex(50);
});
};
return false;
const followOutput = (isAtBottom: boolean): FollowOutputScalarType => {
/**
* ,
*/
return isAtBottom ? 'smooth' : false;
};
const itemContent = (virtuosoIndex: number) => {
const index = virtuosoIndex + numItemsPrepended - PREPEND_OFFSET;
return buildMessageItemRow(props.messages, props.messages[index]._id);
};
return (
<Virtuoso
style={{ height: '100%' }}
style={virtuosoStyle}
ref={listRef as any}
initialTopMostItemIndex={props.messages.length - 1}
data={props.messages}
overscan={100}
itemContent={(index, data) =>
buildMessageItemRow(props.messages, data._id)
}
firstItemIndex={PREPEND_OFFSET - numItemsPrepended}
initialTopMostItemIndex={Math.max(props.messages.length - 1, 0)}
totalCount={props.messages.length}
overscan={200}
itemContent={itemContent}
alignToBottom={true}
startReached={handleLoadMore}
followOutput={followOutput}
/>
);
}
);
VirtualizedMessageList.displayName = 'VirtualizedMessageList';
function usePrependedMessagesCount(messages: ChatMessage[]) {
const currentFirstMessageId = messages?.[0]?._id;
const firstMessageId = useRef(currentFirstMessageId);
const earliestMessageId = useRef(currentFirstMessageId);
const previousNumItemsPrepended = useRef(0);
const numItemsPrepended = useMemo(() => {
if (!messages || !messages.length) {
return 0;
}
// if no new messages were prepended, return early (same amount as before)
if (currentFirstMessageId === earliestMessageId.current) {
return previousNumItemsPrepended.current;
}
if (!firstMessageId.current) {
firstMessageId.current = currentFirstMessageId;
}
earliestMessageId.current = currentFirstMessageId;
// if new messages were prepended, find out how many
// start with this number because there cannot be fewer prepended items than before
for (
let i = previousNumItemsPrepended.current;
i < messages.length;
i += 1
) {
if (messages[i]._id === firstMessageId.current) {
previousNumItemsPrepended.current = i;
return i;
}
}
return 0;
// TODO: there's a bug here, the messages prop is the same array instance (something mutates it)
// that's why the second dependency is necessary
}, [messages, messages?.length]);
return numItemsPrepended;
}

@ -3616,7 +3616,7 @@ cloneable-readable@^1.0.0:
process-nextick-args "^2.0.0"
readable-stream "^2.3.5"
clsx@^1.0.4, clsx@^1.1.1:
clsx@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
@ -4438,7 +4438,7 @@ dom-converter@^0.2.0:
dependencies:
utila "~0.4"
dom-helpers@^5.0.1, dom-helpers@^5.1.3:
dom-helpers@^5.0.1:
version "5.2.1"
resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
@ -9143,11 +9143,6 @@ react-is@^17.0.1:
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/react-lifecycles-compat/download/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha1-TxonOv38jzSIqMUWv9p4+HI1I2I=
"react-native-storage@npm:@trpgengine/react-native-storage@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@trpgengine/react-native-storage/-/react-native-storage-1.0.1.tgz#7571ec0d837150a4eec123ae7b938084b3c70413"
@ -9231,18 +9226,6 @@ react-virtualized-auto-sizer@^1.0.6:
resolved "https://registry.nlark.com/react-virtualized-auto-sizer/download/react-virtualized-auto-sizer-1.0.6.tgz#66c5b1c9278064c5ef1699ed40a29c11518f97ca"
integrity sha1-ZsWxySeAZMXvFpntQKKcEVGPl8o=
react-virtualized@^9.22.3:
version "9.22.3"
resolved "https://registry.nlark.com/react-virtualized/download/react-virtualized-9.22.3.tgz#f430f16beb0a42db420dbd4d340403c0de334421"
integrity sha1-9DDxa+sKQttCDb1NNAQDwN4zRCE=
dependencies:
"@babel/runtime" "^7.7.2"
clsx "^1.0.4"
dom-helpers "^5.1.3"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.4"
react-virtuoso@^2.2.7:
version "2.2.7"
resolved "https://registry.npmmirror.com/react-virtuoso/download/react-virtuoso-2.2.7.tgz?cache=0&sync_timestamp=1636198122025&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Freact-virtuoso%2Fdownload%2Freact-virtuoso-2.2.7.tgz#3aa7243ed79ec6257f0de30679f64e9614533d2e"

Loading…
Cancel
Save