探索 IM 场景中 React Native FlatList 下拉加载更多

757 阅读1分钟

IM 场景和普通场景下的 FlatList 的区别

聊天信息和普通的列表信息不一样,聊天信息列表需要最新的聊天消息显示在最底部,初始展现的聊天信息是最新的聊天信息,同时,如果消息数目不够占满屏幕,整个 FlatList 应该在屏幕顶部。

实现 IM 场景下 FlatList 的思路

添加 FlatList inverted 属性为 true 后,整个 FlatList 都翻转,包括了上拉下拉的回调函数也翻转了,现在上拉到顶实际会触发 onEndReached 回调。

当 invert FlatList 后,如果 items 数量不够占满屏幕,items 会出现在屏幕底部,设置 FlatList 的 contentContainerStyle 属性使 items 出现在屏幕顶部,注意 flexGrow: 1 会导致额外触发一次 onEndReached

contentContainerStyle={{
  flexGrow: 1,
  justifyContent: 'flex-end',
}}

效果展示:

CleanShot 2023-07-05 at 20.21.14.gif

完整的测试代码:

import {Text, FlatList, StyleSheet} from 'react-native';
import React, {useRef} from 'react';

/**
 *  100 条历史消息,先给 10 条最新的,再给 10 条
 *  也就是先给 91 - 100, 再给 81 - 90, 依次类推
 */
const getData = (page: number, size: number) => {
  return new Promise<string[]>(resolve => {
    setTimeout(() => {
      const allData = [];
      for (let i = 0; i < 200; i++) {
        allData.push(`Message ${i}`);
      }

      allData.reverse();

      const newRes = allData.slice(page * size, page * size + size).reverse();

      resolve(newRes);
    }, 1000);
  });
};

const DetailScreen = () => {
  /**
   * 分页字段
   * page
   * size
   * total,根据 total 判断是否还有更多数据
   */

  const curPageRef = useRef(0);
  const SIZE = 15;
  const [data, setData] = React.useState<string[]>([]);

  const handleOnEndReached = () => {
    console.log(
      '🚀 ~ file: DetailScreen.tsx:53 ~ handleOnEndReached ~ handleOnEndReached:',
      handleOnEndReached,
    );

    getData(curPageRef.current, SIZE).then(res => {
      res.reverse();
      setData([...data, ...res]);
    });
    curPageRef.current += 1;
  };

  return (
    <>
      <FlatList
        onEndReached={handleOnEndReached}
        style={{flex: 1}}
        contentContainerStyle={{
          flexGrow: 1,
          justifyContent: 'flex-end',
        }}
        inverted
        data={data}
        renderItem={({item}) => <Text style={styles.message}>{item}</Text>}
        keyExtractor={item => item}
      />
    </>
  );
};

const styles = StyleSheet.create({
  message: {
    fontSize: 30,
    flex: 1,
    margin: 10,
    backgroundColor: '#FFF',
  },
});

export default DetailScreen;