请教一个 react native FlatList 的问题

2025 年 2 月 18 日
 letterLim
正在做一个聊天应用。 因为要有双向滚动加载的需求放弃了 FlashList 转而使用 FlatList 。 发现发消息新增 item 时。每次渲染都需要 0.8 秒左右。在这期间所有的 setState 都会卡住。 也试了文档中的优化方案感觉效果不大。 有没有大哥知道怎么解决
2063 次点击
所在节点    React
4 条回复
donotquestion
2025 年 2 月 19 日
放弃吧,我研究了两周放弃了,傻逼 RN 不会按照你的想法的顺序走的,跳来跳去,双向滚动超级大坑
iOCZS
2025 年 2 月 19 日
相关代码看看?
jingrui
2025 年 2 月 19 日
我们还是用的 FlashList ,滚动到指定消息 很难优化


const firstScrollRef = useRef(false);

function scrollToMsg(msgID){
let index = viewMessages.findIndex((value)=>value?.msgID === msgID)
if (index <= 0) index = 1;
console.log('scrollToMsg', index, msgID, viewMessages.length);
flatListRef.current.scrollToIndex({ index:index-1, animated: true });
}

const scrollToBottom = useCallback(
debounce(() => {
if (flatListRef.current) {
console.log('scrollToBottom', firstScrollRef.current, from, msgID)
IMSdk.readConversation(uid)
if (!firstScrollRef.current) {
if (from === 'history' && msgID) {
scrollToMsg(msgID)
} else {
flatListRef.current.scrollToEnd({ animated: true });
}
firstScrollRef.current = true
} else {
flatListRef.current.scrollToEnd({ animated: true });
}
}
}, 200), // 延迟时间可以根据需要调整
[from, msgID]
);
letterLim
2025 年 2 月 19 日
const getMessageList = useMemo(() => (startPage: number, endPage: number) => {
return allMessageList
.slice(startPage * PAGE_SIZE, (endPage + 1) * PAGE_SIZE)
}, [allMessageList, PAGE_SIZE])

useEffect(() => {
setMessageList(getMessageList(0, 0))
}, [])

useEffect(() => {
if (startPage === 0 && isAtBottom) {
setMessageList(getMessageList(startPage, endPage));
}
}, [startPage, endPage, getMessageList, isAtBottom]);


const onReachTop = useCallback(() => {
console.log('onReachTop');
if (isJumpingToUnread) {
return;
}
if (startPage > 0) {
const _startPage = startPage - 1;
setStartPage(_startPage);
setMessageList(getMessageList(_startPage, endPage))
}
}, [startPage, isJumpingToUnread, endPage, getMessageList]);


const onReachBottom = useCallback(() => {
console.log('onReachBottom', isJumpingToUnread);
if (isJumpingToUnread) {
return;
}
if (messageList.length < allMessageList.length) {
setEndPage(pre => {
setMessageList(getMessageList(startPage, pre + 1))
return pre + 1
})
}
}, [messageList.length, setEndPage, startPage, allMessageList.length, isJumpingToUnread, getMessageList]);

const goUnreadMessage = useCallback(() => {
setIsJumpingToUnread(true);
setUnreadNum(0)
let _messageList: any = []
const startPage = Math.floor(unreadNum / PAGE_SIZE) > 1 ? Math.floor(unreadNum / PAGE_SIZE) - 1 : 0;
let endPage = startPage + 1;
_messageList = getMessageList(startPage, endPage)
const index = _messageList.findIndex(item => item._id === lastMessageId.current)
if (index % PAGE_SIZE === 0) {
endPage = startPage + 1
}
setStartPage(startPage);
setEndPage(endPage);
setMessageList(_messageList)
}, [allMessageList.length, getMessageList]);

useEffect(() => {
if (isJumpingToUnread && lastMessageId.current) {
const index = messageList.findIndex(item => item._id === lastMessageId.current)
if (index !== -1) {
const timer = setTimeout(() => {
clearTimeout(timer)
flatListRef.current?.scrollToIndex({
index,
animated: true,
viewPosition: 0.1,
})
}, 200)
}
}
}, [messageList.length, isJumpingToUnread]);


useEffect(() => {
if (!lastMessageId.current && allMessageList.length >= (unreadNum-1) && unreadNum) {
lastMessageId.current = allMessageList[unreadNum-1]?._id;
}
}, []);

useEffect(() => {
const initMessageList = () => {
setStartPage(0)
setEndPage(0)
setMessageList(getMessageList(0, 0))
InteractionManager.runAfterInteractions(() => {
flatListRef?.current?.scrollToOffset({ offset: 0, animated: true })
})
}
EventCenter.on('init_message_list', initMessageList)
return () => {
EventCenter.remove('init_message_list', initMessageList)
}
}, [getMessageList])


const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
const isBottom = event.nativeEvent.contentOffset.y <= 10;
setIsAtBottom(isBottom);
}, []);

const onScrollToIndexFailed = useCallback((info: {
index: number;
highestMeasuredFrameIndex: number;
averageItemLength: number;
}) => {
const offset = info.index * info.averageItemLength
flatListRef.current?.scrollToOffset({ offset: offset, animated: true })
}, []);

const threshold = isJumpingToUnread ? 0.1 : 1

<FlatList
removeClippedSubviews
windowSize={20}
onStartReachedThreshold={threshold}
onEndReachedThreshold={threshold}
onEndReached={onReachBottom}
onStartReached={onReachTop}
onScroll={handleScroll}
scrollEventThrottle={17}
keyboardDismissMode="on-drag"
maxToRenderPerBatch={10}
initialNumToRender={10}
onTouchStart={() => {
setIsMore(false)
setEmojiKeyboardVisible(false)
}}
onScrollBeginDrag={() => {
setIsJumpingToUnread(false);
}}
maintainVisibleContentPosition={startPage && !isAtBottom ? {
minIndexForVisible: 1,
autoscrollToTopThreshold: 10
}:undefined}
onScrollToIndexFailed={onScrollToIndexFailed}
inverted
contentContainerStyle={{ paddingHorizontal: 10 }}
scrollsToTop={false}
data={messageList}
keyExtractor={item => item._id}
showsVerticalScrollIndicator
ref={flatListRef}
renderItem={renderItem}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1112454

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX