如何在FlatList和React Hook中使用Infinite Scroll?

时间:2019-10-08 10:00:32

标签: reactjs react-native

我正在重构React Hooks,但是在FlatList正常工作的情况下我无法获得无限滚动。

  const [page, setPage] = useState(1);

这是我的useEffect Hook:

useEffect(() => {
    const loadProducts = async () => {
      setIsLoading(true);
      let response = await fetch(
        `${api}&page=${page}&perPage=5`
      );
      let results = await response.json();
      setProducts([...products, ...results.data]);
      setIsLoading(false);
    };
    loadProducts();
  }, [page]);

偏移量为$ {page},限制为&perPage = 5(硬编码为5)

平面列表:

<FlatList
      data={products}
      keyExtractor={item => item.id}
      renderItem={renderGridItem}
      onEndReached={loadMore}
      onEndThreshold={0.3}
    />

LoadMore:

  const loadMore = () => {
    setPage(page + 1);
  };

从理论上讲,这应该起作用,不是吗?

2 个答案:

答案 0 :(得分:0)

在这种情况下,请尝试使用useCallback代替useEffect。另外,我还向您展示了如何防止将空结果传播到setState

const loadProducts = async () => {
  setIsLoading(true);
  let response = await fetch(`${api}&page=${page}&perPage=5`);
  let results = await response.json();
  if (result.data) {
    setProducts([...products, ...results.data]);
  }
  setIsLoading(false);
};

useEffect(() => {
  loadProducts();
}, [])

const onLoadMore = useCallback(() => {
  loadProducts();
}

有关useCallback的更多信息,请阅读此内容。 https://reactjs.org/docs/hooks-reference.html#usecallback

答案 1 :(得分:0)

说明

我自己为此付出了很多努力。这是一个使用SectionList(与Flatlist基本相同)的示例

标头编号表示发送到API的请求编号。您可以通过单击“检查号码”按钮来检查请求的顺序正确且没有重复。

在此示例中,我们使用reqres.in模拟对某些数据的提取。

该示例还实现了“推入刷新”。再次,您可以通过单击“检查长度”按钮来检查上拉刷新后数组的长度是否符合预期。

博览会小吃

A snack of the example can be found here: https://snack.expo.io/BydyF9yRH

请确保在零食中将平台更改为iOS或Android(网络将无法使用)

代码

import * as React from 'react';
import { ActivityIndicator } from 'react-native'
var _ = require('lodash')

import {
    StyleSheet,
    Text,
    View,
    SafeAreaView,
    SectionList,
    Button,
    RefreshControl
} from 'react-native';

function Item(item) {
    return (
        <View style={styles.item}>
            <Text style={styles.title}>{item.title.first_name}</Text>
        </View>
    );
}

export default function testSectionList({ navigation }) {

    const [data, setData] = React.useState()
    const [loading, setLoading] = React.useState(true)
    const [refreshing, setRefreshing] = React.useState(false);
    const [showRefreshingIndicator, setShowRefreshingIndicator] = React.useState(false);

    const dataIndex = React.useRef(0);
    const totalHits = React.useRef(42); // In real example: Update this with first result from api

    const fetchData = async (reset: boolean) => {

        if (reset === true) dataIndex.current = 0;

        // Make sure to return if no more data from API
        if (dataIndex.current !== 0 && dataIndex.current >= totalHits.current) return []

        // For example usage, select a random page
        const fakepage = Math.round(Math.random()) * 2
        const resultObject = await fetch(`https://reqres.in/api/users?page=${fakepage}`);
        const result = await resultObject.json()

        dataIndex.current++;

        return {
            title: `${dataIndex.current-1}`,
            data: await result.data
        }
    }

    const count = () => {
        alert(data.length)
    }

    const checkPageNumbers = () => {
        const numbers = data.map(item => parseInt(item.title))
        const incremental = [...Array(data.length).keys()]

        alert(_.isEqual(numbers, incremental))
    }

    const getInitialData = async () => {

        const list = await fetchData(false)
        if(!list) return

        setData([list])

        setLoading(false)
    }

    React.useEffect(() => {
        getInitialData()
    }, [])

    const onEndReached = async () => {
        const newItems = await fetchData(false)
        if(!newItems.data.length) return
        setData([...data, newItems])
    }

    const onRefresh = React.useCallback(async () => {
        setShowRefreshingIndicator(true);

        const newItems = await fetchData(true)
        setData([newItems])

        setShowRefreshingIndicator(false)

    }, [refreshing]);

    if (loading) return <Text>Loading...</Text>

    return (
        <SafeAreaView style={styles.container}>
            <Button title={"Check numbers"} onPress={() => checkPageNumbers()} />
            <Button title={"Check length"} onPress={() => count()} />
            <SectionList
                sections={data}
                refreshing={refreshing}
                refreshControl={
                    <RefreshControl refreshing={showRefreshingIndicator} onRefresh={onRefresh} />
                }
                onEndReached={() => {
                    if(refreshing) return;
                    setRefreshing(true)
                    onEndReached().then(() => {
                        setRefreshing(false)
                    })
                }}
                onEndReachedThreshold={1}
                keyExtractor={(item, index) => item + index}
                renderItem={({ item }) => <Item title={item} />}
                renderSectionHeader={({ section: { title } }) => (
                    <Text style={styles.header}>{title}</Text>
                )}
                ListFooterComponent={<ActivityIndicator size={"large"} />}
                stickySectionHeadersEnabled={false}
            />
        </SafeAreaView>
    );

}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 40,
        marginHorizontal: 16,
    },
    item: {
        backgroundColor: '#f9c2ff',
        padding: 2,
        marginVertical: 2,
    },
    header: {
        fontSize: 16,
    },
    title: {
        fontSize: 12,
    },
});