在 'Flatlist' 中追加数据而不刷新本机中的完整 'Flatlist'

Append Data in 'Flatlist' without refreshing complete 'Flatlist' in react native

FlatList自带选项,当listview结束时,从API中取出下一组数据,我们也可以使用scrollTo函数来保留Flatlist 的最后滚动位置,但以这种方式实现是在每个下一个分页数据请求时创建 FlatList 的重新加载下降,完整的 listview 加载并且我们滚动到最后一个左侧位置。无论如何,如果我们可以实现一项功能,其中 Flatlist 中的现有数据不会被刷新,并且只有新数据附加到底部而不改变 React Native 中的滚动位置,这将有助于以更优化的方式呈现 FlastList。对建议的任何帮助将不胜感激。

这是我的理解。

您有一个 FlatList 并从 API 获取数据。

onEndReached 您正在获取更多数据,但是当您将此数据传递给 FlatList 时,整个列表会刷新并且您会丢失滚动位置。

我做了类似的事情,但在附加数据时我的滚动位置保持不变。

以下是对我有用的。为了简单起见,我只指定数据部分。

const [items, setItems] = useState([]);

<FlatList 
    data={items} 
    onEndReached={() => setItems(items.concat(newData))}
/>

想法是将您的新数据联系到现有数据数组。

如果有帮助,请告诉我。

React Native 中的 FlatList 组件有一个名为 onEndReached 的 prop。当用户到达列表末尾时调用此道具。这是从 API 中获取下一组数据而无需刷新整个列表的简单方法。

FlatList 组件还有一个名为 scrollToIndex 的 prop。此道具允许您滚动到列表中的特定索引。如果您想保留 FlatList 的最后滚动位置,这很有用。

您可以在 React Native 文档中了解有关 FlatList 组件的更多信息:

https://reactnative.dev/docs/flatlist

我是这样实现的:

每次用户向下滚动到底部并相应地改变状态时,我都会从数据库中获取 10 条记录。基本上,它在 mysql 数据库中使用 limit 关键字,如 limit 0, 10 或 limit 10, 10 来获取数据块。当一个新的数据块添加到当前数据的末尾时,我没有改变滚动条的位置,也没有改变现有的数据。

我将与您分享我的代码,很难解释它的每一行,但我认为它可以让您了解我在说什么。

this.state = {
  list_total: 0,
  order_list: [],
  order_detail_list: [],
  modal_visible: false,
  modal_type: null,
  mode: 'date',
  start_date: date.setMonth(date.getMonth() - 36),
  start_date_show: false,
  end_date: new Date(),
  end_date_show: false,
  date_modal: true,
  scan_modal_visible: false,
  ready_to_insert_last_material_type_value: '',
  ready_to_insert_last_status_value: '',
  ready_to_insert_last_sales_representative_value: '',
  last_material_type_value: '',
  last_status_value: '',
  last_sales_representative_value: '',
  ready_to_insert_last_filter_value: 7,
  last_filter_value: 7,
  ready_to_insert_last_sort_value: 6,
  last_sort_value: 6,
  limit: 10,
  offset: 0,
  loading: false,
  stop: false,
  handlingMore : false,
  refreshing: false
};

平面列表

<FlatList
  extraData={this.state}
  data={this.state.order_list}
  keyExtractor={(item, index) => index.toString()}
  keyboardShouldPersistTaps="always"
  initialNumToRender={10} // Reduce initial render amount
  maxToRenderPerBatch={5} // Reduce number in each render batch
  updateCellsBatchingPeriod={100} // Increase time between renders
  onMomentumScrollBegin = {() => {this.onEndReachedCalledDuringMomentum = false;}}
  onEndReached={() => {
    if (!this.onEndReachedCalledDuringMomentum && !this.state.handlingMore) 
    {
      this.setState({
        handlingMore: true
      }, () => {
        this.handleLoadMore();
                                                  
        this.onEndReachedCalledDuringMomentum = true;
      });
    }
  }}
  onEndReachedThreshold = {0.5}
  ListFooterComponent={() =>
    !this.state.loading ? null
    : (this.state.stop ? null : <ActivityIndicator size="large" animating />
  )}
  renderItem={({item}) => <OrderListItem item={item} order_detail_list={this.state.order_detail_list} />}
  refreshing={this.state.refreshing} 
  onRefresh={this.handleRefresh}
/>

handleRefresh: 刷新屏幕的方法,它从数据库中获取前10行(如果至少有10条记录,可能会更少)

handleLoadMore:获取下一个数据块的方法(如果存在,可能是10行或更少)

handleRefresh = (isApplyButton) => {
  if (isApplyButton)
    this.props.setFullLoading(true);

    this.setState({
      offset: 0,
      stop: false,
      refreshing: true,
      handlingMore: false,
    }, () => {
      let obj = {
        company_uid: this.props.company_uid,
        user_uid: this.props.user_uid,
        sort_filter: this.props.sort_filter_orders,
        limit: this.state.offset + ',' + this.state.limit
    };

    Promise.all([this.props.getOrderList(obj),this.props.getOrderDetailList(obj)])
      .then(resultArray => {
        this.setState(state => ({
          list_total: resultArray[0].length,
          order_list: resultArray[0],
          order_detail_list: resultArray[1],
          refreshing: false
        }));
        if (isApplyButton) {
          if (this.props.full_loading) {
            this.props.setFullLoading(false);
          }
        }
      })
      .catch(error => {
        WToast.show({data: lang.ERROR});
        console.log(error);
      });
    });
};

_getOrderList = async () => {
  this.setState({
    loading: true
  });
  let obj = {
    company_uid: this.props.company_uid,
    user_uid: this.props.user_uid,
    sort_filter: this.props.sort_filter_orders,
    limit: this.state.offset + ',' + this.state.limit
  };

  Promise.all([this.props.getOrderList(obj),this.props.getOrderDetailList(obj)])
    .then(resultArray => {
      this.setState(state => ({
        order_list: [...state.order_list, ...resultArray[0]],
        order_detail_list: resultArray[1],
        loading: false,
        stop: resultArray[0].length < this.state.limit
      }), () => 
        this.setState({list_total: this.state.order_list.length, handlingMore: false}));
        if (this.props.full_loading) {
          this.props.setFullLoading(false);
        }
      })
      .catch(error => {
        WToast.show({data: lang.ERROR});
        console.log(error);
    });
};

handleLoadMore = () => {
  if (this.state.stop)
    return;

  this.setState(state => ({offset: state.offset + 10}), () =>  this._getOrderList());
};

减速动作

export const getOrderList = (obj) => {
    return (dispatch) => {
        const promise = new Promise((resolve, reject) => {
            const post_data = {
                function: 'Get_fetch_all',
                token: SEND_POST_TOKEN,
                sql_query: 'SELECT o.uid, o.date, o.document_no, IFNULL(o.description, \'\') description, CASE o.status WHEN 0 THEN \'Açık\' WHEN 1 THEN \'Kapalı\' WHEN 2 THEN \'İptal\' WHEN 3 THEN \'İstenildi\' WHEN 4 THEN \'Teslim Edildi\' ELSE \'İade Edildi\' END status, CASE o.material_type WHEN 0 THEN \'Kartela\' WHEN 1 THEN \'Numune\' ELSE \'Model\' END material_type\n' +
                    'FROM orders o\n' +
                    'LEFT JOIN (SELECT od.company_uid, od.uid, od.order_uid, od.no, od.material_uid, od.quantity, od.unit,\n' +
                    '                  CASE WHEN material_type = 0 THEN sc.code\n' +
                    '                       WHEN material_type = 1 THEN s.code\n' +
                    '                       WHEN material_type = 2 THEN m.code END material_code\n' +
                    '            FROM order_details od\n' +
                    '                     INNER JOIN orders o ON (od.company_uid = o.company_uid AND od.order_uid = o.uid)\n' +
                    '                     LEFT JOIN swatch_cards sc ON (od.company_uid = sc.company_uid AND od.material_uid = sc.uid)\n' +
                    '                     LEFT JOIN users u ON (o.company_uid = u.company_uid AND o.sales_representative_uid = u.uid)\n' +
                    '                     LEFT JOIN (SELECT s.company_uid, s.uid, s.code, v.variant FROM samples s INNER JOIN variants v ON (s.company_uid = v.company_uid AND s.variant_uid = v.uid)) s ON (od.company_uid = s.company_uid AND od.material_uid = s.uid)\n' +
                    '                     LEFT JOIN (SELECT m.company_uid, m.uid, m.code, v.variant FROM models m INNER JOIN variants v ON (m.company_uid = v.company_uid AND m.variant_uid = v.uid)) m ON (od.company_uid = m.company_uid AND od.material_uid = m.uid)\n' +
                    ') m ON (o.company_uid = m.company_uid AND o.uid = m.order_uid)\n' +
                    'WHERE o.company_uid = :company_uid AND (DATE(o.date) BETWEEN :filter_start_date AND :filter_end_date) AND\n' +
                    'CASE WHEN (o.material_type = 0 AND (m.material_code = :material_code OR :material_code = \'\')) OR (o.material_type = 1 AND (m.material_code = :material_code OR :material_code = \'\')) OR (o.material_type = 2 AND (m.material_code = :material_code OR :material_code = \'\')) THEN 1 ELSE 0 END = 1\n' +

                    'AND (CAST(o.material_type AS CHAR) IN (\n' +
                    'SELECT CASE WHEN swatch_card_auth = 1 THEN \'0\' ELSE \'\' END\n' +
                    'FROM users WHERE uid = :user_uid\n' +
                    ') OR CAST(o.material_type AS CHAR) IN (\n' +
                    'SELECT CASE WHEN sample_auth = 1 THEN \'1\' ELSE \'\' END\n' +
                    'FROM users WHERE uid = :user_uid\n' +
                    ') OR CAST(o.material_type AS CHAR) IN (\n' +
                    'SELECT CASE WHEN model_auth = 1 THEN \'2\' ELSE \'\' END\n' +
                    'FROM users WHERE uid = :user_uid\n' +
                    ') OR o.user_uid = :user_uid OR o.sales_representative_uid = :user_uid)',
                fetch_json: {
                    company_uid: obj.company_uid,

                    user_uid: obj.user_uid,

                    filter_start_date: obj.sort_filter.filter_start_date,
                    filter_end_date: obj.sort_filter.filter_end_date,
                    material_code: obj.sort_filter.material_code
                }
            };

            switch (obj.sort_filter.filter) {
                case 8:
                    post_data.fetch_json.material_type = obj.sort_filter.filter_obj.materialTypeValue;
                    post_data.sql_query += ' AND (o.material_type = :material_type OR :material_type = \'\')';
                    post_data.fetch_json.status = obj.sort_filter.filter_obj.statusValue;
                    post_data.sql_query += ' AND (o.status = :status OR :status = \'\')';
                    post_data.fetch_json.sales_representative_uid = obj.sort_filter.filter_obj.salesRepresentativeValue;
                    post_data.sql_query += ' AND (o.sales_representative_uid = :sales_representative_uid OR :sales_representative_uid = \'\')';
                    break;
            }

            post_data.sql_query += ' GROUP BY o.uid, o.date, o.document_no, o.material_type, o.created_date';

            switch (obj.sort_filter.sort) {
                case 1:
                    post_data.sql_query += ' ORDER BY o.material_type ASC, o.created_date ASC, o.document_no ASC';
                    break;

                case 2:
                    post_data.sql_query += ' ORDER BY o.material_type DESC, o.created_date DESC, o.document_no DESC';
                    break;

                case 3:
                    post_data.sql_query += ' ORDER BY o.document_no ASC, o.created_date ASC';
                    break;

                case 4:
                    post_data.sql_query += ' ORDER BY o.document_no DESC, o.created_date DESC';
                    break;

                case 5:
                    post_data.sql_query += ' ORDER BY o.created_date ASC, o.document_no ASC';
                    break;

                case 6:
                    post_data.sql_query += ' ORDER BY o.created_date DESC, o.document_no DESC';
                    break;
            }

            post_data.sql_query += ' LIMIT '  + obj.limit;

            axios.post(SERVICE_API_URL, post_data, postHeaderUserAgent)
                .then((response) => {
                    resolve(response.data.fetch_all);
                })
                .catch((error) => {
                    reject(error);
                });
        });
        return promise
            .then((response) => {
                return response;
            }).catch(error => {
                throw(new Error(error));
            });
    }
};

结果 https://imgur.com/a/8qM5PQN

分页示例:-

class FidsListview extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      studentData: [],
      currentPage: 0,
      lastPage: 0,
    };

    this.getChildrenApi = this.getChildrenApi.bind(this);
  }

  componentDidMount() {
    this.getChildrenApi();
  }

  getChildrenApi() {
    let currentPage = this.state.currentPage + 20;
    fetch(
      `https://fids.myairports.com.my/api/flights/get-flight-departures-cache?skip=${currentPage}&take=${
        currentPage + 20
      }&terminal=KLIA`
    )
      .then((result) => result.json())
      .then((res) => {
        if (
          res.flightStatuses != null &&
          res.flightStatuses != undefined &&
          res.flightStatuses.length > 0
        ) {
          this.setState({
            studentData: res.flightStatuses,
          });
        } else {
          this.setState({ studentData: [] });
        }
      });
  }

  lodeMoreData() {
    let nextPage = this.state.currentPage + 20;

    fetch(
      `http://paginationapi.com?skip=${nextPage}&take=${
        nextPage + 20
      }&terminal=KLIA`
    )
      .then((result) => result.json())
      .then((res) => {
        if (
          res.flightStatuses != null &&
          res.flightStatuses != undefined &&
          res.flightStatuses.length > 0
        ) {
          this.setState({
            studentData: [...this.state.studentData, ...res.flightStatuses],
            currentPage: nextPage,
          });
        } else {
          this.setState({ studentData: [] });
        }
      });
  }

  render() {
    return (
      <View style={{}}>
        <FlatList
          style={{ backgroundColor: "#FFF" }}
          showsVerticalScrollIndicator={false}
          data={this.state.studentData}
          numColumns={3}
          ListFooterComponent={() => <View style={{ marginBottom: 300 }} />}
          extraData={this.state.studentData}
          renderItem={({ item, index }) => (
            <View style={{ height: 100, backgroundColor: "#EEE" }}>
              <Text>{item.afsKey}</Text>
            </View>
          )}
         
        />
        {this.state.isLoading && (
          <View
            style={{
              width: "100%",
              height: "100%",
              alignItems: "center",
              justifyContent: "center",
              position: "absolute",
            }}
          >
            <ActivityIndicator size="large" color={"white"} />
          </View>
        )}
      </View>
    );
  }
}

export def

故障 FidsListview;