mirror of https://github.com/msgbyte/tailchat
refactor: 迭代自实现虚拟列表并升级react-virtuoso
parent
f038d08af8
commit
9500a2fab0
@ -1,14 +1,82 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
|
import { ResizeWatcher } from './ResizeWatcher';
|
||||||
import { Scroller, ScrollerRef } from './Scroller';
|
import { Scroller, ScrollerRef } from './Scroller';
|
||||||
|
import { useUpdate } from 'ahooks';
|
||||||
|
|
||||||
export const VirtualChatList: React.FC = React.memo(() => {
|
interface VirtualChatListProps<ItemType> {
|
||||||
|
className?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
innerStyle?: React.CSSProperties;
|
||||||
|
getItemKey?: (item: ItemType) => string;
|
||||||
|
items: ItemType[];
|
||||||
|
itemContent: (item: ItemType, index: number) => React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultContainerStyle: React.CSSProperties = {
|
||||||
|
overflow: 'hidden',
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultInnerStyle: React.CSSProperties = {
|
||||||
|
height: '100%',
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollerStyle: React.CSSProperties = {
|
||||||
|
height: '100%',
|
||||||
|
};
|
||||||
|
|
||||||
|
const InternalVirtualChatList = <ItemType extends object>(
|
||||||
|
props: VirtualChatListProps<ItemType>
|
||||||
|
) => {
|
||||||
const scrollerRef = useRef<ScrollerRef>(null);
|
const scrollerRef = useRef<ScrollerRef>(null);
|
||||||
|
const itemHeightCache = useMemo(() => new Map<ItemType, number>(), []);
|
||||||
|
const forceUpdate = useUpdate();
|
||||||
|
const style = useMemo(
|
||||||
|
() => ({
|
||||||
|
...defaultContainerStyle,
|
||||||
|
...props.style,
|
||||||
|
}),
|
||||||
|
[props.style]
|
||||||
|
);
|
||||||
|
const innerStyle = useMemo(
|
||||||
|
() => ({
|
||||||
|
...defaultInnerStyle,
|
||||||
|
...props.innerStyle,
|
||||||
|
}),
|
||||||
|
[props.innerStyle]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 挂载后滚动到底部
|
||||||
|
scrollerRef.current?.scrollToBottom();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scroller ref={scrollerRef}>
|
<div className="virtual-chat-list" style={style}>
|
||||||
{/* TODO */}
|
<Scroller ref={scrollerRef} style={scrollerStyle} innerStyle={innerStyle}>
|
||||||
<div>Foo</div>
|
{props.items.map((item, i) => (
|
||||||
</Scroller>
|
<div
|
||||||
|
key={props.getItemKey ? props.getItemKey(item) : i}
|
||||||
|
className="virtual-chat-list__item"
|
||||||
|
style={{ height: itemHeightCache.get(item) }}
|
||||||
|
>
|
||||||
|
<ResizeWatcher
|
||||||
|
onResize={(size) => {
|
||||||
|
itemHeightCache.set(item, size.height);
|
||||||
|
forceUpdate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.itemContent(item, i)}
|
||||||
|
</ResizeWatcher>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Scroller>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
type VirtualChatListInterface = typeof InternalVirtualChatList & React.FC;
|
||||||
|
|
||||||
|
export const VirtualChatList: VirtualChatListInterface = React.memo(
|
||||||
|
InternalVirtualChatList
|
||||||
|
) as any;
|
||||||
VirtualChatList.displayName = 'VirtualChatList';
|
VirtualChatList.displayName = 'VirtualChatList';
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { VirtualChatList } from 'tailchat-design';
|
||||||
|
import { ChatMessage, useMemoizedFn } from 'tailchat-shared';
|
||||||
|
import { buildMessageItemRow } from './Item';
|
||||||
|
import type { MessageListProps } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WIP:
|
||||||
|
* 自制的虚拟列表
|
||||||
|
*/
|
||||||
|
|
||||||
|
const style: React.CSSProperties = {
|
||||||
|
// height: '100%',
|
||||||
|
flex: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VirtualizedMessageList: React.FC<MessageListProps> = React.memo(
|
||||||
|
(props) => {
|
||||||
|
// useSharedEventHandler('sendMessage', () => {
|
||||||
|
// listRef.current?.scrollToIndex({
|
||||||
|
// index: 'LAST',
|
||||||
|
// align: 'end',
|
||||||
|
// behavior: 'smooth',
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const handleLoadMore = useMemoizedFn(() => {
|
||||||
|
// if (props.isLoadingMore) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (props.hasMoreMessage) {
|
||||||
|
// props.onLoadMore();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
const itemContent = useMemoizedFn((item: ChatMessage, index: number) => {
|
||||||
|
return buildMessageItemRow(props.messages, index);
|
||||||
|
});
|
||||||
|
|
||||||
|
const getItemKey = useCallback((item: ChatMessage) => {
|
||||||
|
return String(item._id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtualChatList
|
||||||
|
style={style}
|
||||||
|
items={props.messages}
|
||||||
|
itemContent={itemContent}
|
||||||
|
getItemKey={getItemKey}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
VirtualizedMessageList.displayName = 'VirtualizedMessageList';
|
Loading…
Reference in New Issue