React Native FlatList for Chat Application to Instantly Show Latest Messages

27 Views Asked by At

I'm developing a chat application in React Native and facing an issue with rendering many messages in a FlatList. I want to display the latest message at the bottom of the list, similar to how WhatsApp and Messenger work. I've tried several methods, but none have provided the smooth experience I'm looking for:

  1. (Method 01) Inverted List: This method is also not working for a large amount of messages because it takes time to reach there, and the user can come and go any time this screen so the user can't wait that much longer.
   <FlatList
     inverted
     contentContainerStyle={{
       flexGrow: 1,
       flexDirection: 'column-reverse',
     }}
     ref={flatListRef}
     data={chat}
     keyExtractor={keyExtractor}
     renderItem={renderMessage}
     ItemSeparatorComponent={renderSeperator}
   />
  1. (Method 02) Initial Index: Using initialScrollIndex requires a fixed item height, which I don't have, so this method is not feasible for my case, Here is how I used but the ITEM_HEIGHT is not gonna fixed so it will cost a lot of performance in max messages.
 <FlatList
    ref={flatListRef}
    data={chat}
    keyExtractor={keyExtractor}
    renderItem={renderMessage}
    ItemSeparatorComponent={renderSeperator}
    initialScrollIndex={chat.length - 1}
    getItemLayout={(_, index) => {
      return {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index};
    }}
  />
  1. (Method 03) Scroll to the Bottom on Content Size Change: Using scrollToBottom in combination with onContentSizeChange also takes time to reach the bottom when there are 1000 messages.
  const scrollToBottom = (isAnimated = false) => {
    if (flatListRef.current) {
      flatListRef.current.scrollToEnd({animated: isAnimated});
    }
  };

  return (
    <FlatList
      ref={flatListRef}
      data={chat}
      keyExtractor={keyExtractor}
      renderItem={renderMessage}
      ItemSeparatorComponent={renderSeperator}
      onContentSizeChange={() => scrollToBottom(false)} // Doesn't matter if i use animated or not
    />
  );

I am looking for a way to optimize the FlatList to instantly show the latest message without any lag, even with a large number of messages. I'm open to suggestions like removing older messages as the user scrolls up, but I'm not sure how to implement that efficiently. Any guidance or alternative approaches to achieve a smooth chat experience would be greatly appreciated.

1

There are 1 best solutions below

0
Hamza Hussain On

With the inverted prop it worked. There was a simple trick, I had to reverse my messages array too using the reverse() function and also I had to use the scrollToTop function in order to scroll to the bottom. Here is how I made my chat screen application, with a very smooth look.

const DUMMY_CHAT = new Array(10000).fill(null).map((_, i) => ({message: i.toString()}))

export default () => {
  const [inputValue, setInputValue] = useState('');
  const flatListRef = useRef(null);
  const [chat, setChat] = useState(DUMMY_CHAT);

  const renderMessage = ({item, index}) => <ChatMsg {...item} />;
  const renderSeperator = useCallback(() => <Spacer height={24} />, []);
  const keyExtractor = useCallback((_, i) => i.toString(), []);

  // This function will now actually scroll to the bottom.
  // Now you can use this function suppose sending new message to scroll to the bottom.
  const scrollToTop = (isAnimated = false) => {
    if (flatListRef.current) {
      flatListRef.current.scrollToOffset({offset: 0, animated: isAnimated});
    }
  };

  // Add new chat to the start of your list must in order to see new message at bottom
  const onSendPress = () => {
    if (inputValue.trim() === '') return;
    setChat([{message: inputValue}, ...chat]);
    scrollToTop(true); // Enable animation and scroll to bottom of list.
    setInputValue('');
  };

  return (
    <FlatList
      inverted
      ref={flatListRef}
      data={chat.reverse()}
      istHeaderComponent={ChatInternalHeader}
      keyExtractor={keyExtractor}
      renderItem={renderMessage}
      ItemSeparatorComponent={renderSeperator}
    />
  );
}