最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

react native - How to preserve scroll position in FlashList with inverted when loading earlier messages? - Stack Overflow

programmeradmin1浏览0评论

I am building a chat application in React Native using FlashList from @shopify/flash-list. My goal is to load earlier messages when the user scrolls to the top while keeping the scroll position preserved (i.e., no jump or flicker). I want the list to stay in the same visual position after loading new messages, without scrolling to the very end or top.

Here is what I have tried:

  1. Using inverted={true} to reverse the list so that new messages appear at the bottom.
  2. Tracking the content height of the list using onContentSizeChange before and after adding new messages.
  3. Adjusting the scroll offset manually using scrollToOffset to preserve the position.

However, the list still scrolls to the very end after new messages are loaded instead of preserving the user's current position.

My Code:

import {
  loadConversation,
  selectChatState,
} from "@/src/store/features/conversation/conversationSlice";
import { ChatItem } from "@/src/store/features/conversation/types";
import { useAppDispatch, useAppSelector } from "@/src/store/hooks/storeHooks";
import cnStyles from "@/src/styles";
import { FlashList } from "@shopify/flash-list";
import React, { FC, useCallback, useRef } from "react";
import { KeyboardAvoidingView, StyleSheet, View } from "react-native";
import BlockedStrip from "./BlockedStrip";
import InputBox from "./InputBox";
import { MessageBubble } from "./MessageBubble";
import PrevLoader from "./PrevLoader";

type PropType = {
  SendMessage: (value: string) => void;
};

export const ChatList: FC<PropType> = ({ SendMessage }) => {
  const flatListRef = useRef<FlashList<ChatItem>>(null);
  const dispatch = useAppDispatch();
  const loadingMoreRef = useRef(false);

  const { isLoading, messages, start, userInfo, fullyLoaded, you_id } =
    useAppSelector(selectChatState);

  const scrollOffset = useRef(0); // Instead of useSharedValue

  // Function to load earlier messages
  const loadPreviousMessages = useCallback(async () => {
    if (!fullyLoaded && !isLoading && !loadingMoreRef.current) {
      loadingMoreRef.current = true;
      // Capture current scroll position
      const currentOffset = scrollOffset.current;
      try {
        const res = await dispatch(
          loadConversation({
            reqBody: { start, conv_id: you_id },
          })
        ).unwrap();
        if (res.success === "1") {
          // Calculate height based on number of new items
          const newItemsCount = res.user_chat?.length || 0;
          const estimatedItemHeight = 100; // Match your estimatedItemSize
          const heightDifference = newItemsCount * estimatedItemHeight;

          // Adjust scroll after data renders
          setTimeout(() => {
            flatListRef.current?.scrollToOffset({
              offset: currentOffset + heightDifference,
              animated: false,
            });
          }, 0);
        }
      } finally {
        loadingMoreRef.current = false;
      }
    }
  }, [fullyLoaded, isLoading, start, you_id, dispatch]);

  const onSubmitMsg = (msg: string) => {
    SendMessage(msg);
    setTimeout(() => {
      flatListRef.current?.scrollToEnd({ animated: true });
    }, 10);
  };

  return (
    <KeyboardAvoidingView style={cnStyles.container} behavior="padding">
      <View style={styles.messagesList}>
        <FlashList
          ref={flatListRef}
          data={messages}
          keyExtractor={(item) => item.key}
          renderItem={({ item }) => <MessageBubble item={item} />}
          estimatedItemSize={100}
          ListFooterComponent={isLoading ? <PrevLoader /> : null}
          inverted={true} // Keep inverted to support upward scrolling
          onScroll={(event) => {
            scrollOffset.current = event.nativeEvent.contentOffset.y;
          }}
          scrollEventThrottle={16}
          onEndReached={loadPreviousMessages} // Trigger loading earlier messages
          onEndReachedThreshold={0.1} // Trigger when near the top
        />
      </View>
      {userInfo?.button_status === 4 ? (
        <BlockedStrip />
      ) : (
        <InputBox onSubmit={onSubmitMsg} />
      )}
    </KeyboardAvoidingView>
  );
};

Expected Behavior:

  • When new messages are added at the top (i.e., earlier messages), the list should preserve its scroll position without jumping or scrolling to the very bottom.

Current Behavior:

  • After adding new messages, the list scrolls to the very bottom, ignoring the calculated offset.

What I’ve Tried:

  1. Using onContentSizeChange: I capture the content height before and after loading messages and adjust the scroll offset accordingly.

  2. Manual Adjustment of Scroll Offset: I calculate the height difference and add it to the current scrollOffset.

What am I missing in my approach? How can I ensure that the scroll position is preserved when new messages are added with inverted={true}?

Any insights or suggestions would be greatly appreciated!

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论