在React Native中使用Apollo钩子轻松进行无限滚动

徐焱
2023-12-01

Infinite scrolling is a feature that you can see in a lot of modern apps(Facebook, Tweeter, Reddit, etc…), every time the list data in your app gets bigger you will need to add this feature to optimize your list performance.

无限滚动是您可以在许多现代应用程序(Facebook,Tweeter,Reddit等)中看到的功能,每次应用程序中的列表数据变大时,您都需要添加此功能以优化列表性能。

So In this article, we’ll implement the infinite scrolling using apollo hooks

因此,在本文中,我们将使用阿波罗钩子实现无限滚动

Note: I expect that you already have a react-native project initialized with apollo client, also I’ll be using the GitHub Graphql API as an endpoint to display a list of repositories, please also make sure to create a personal access token here and attach it in the apollo header.

注意:我希望您已经有一个使用apollo客户端初始化的react-native项目,我还将使用GitHub Graphql API作为端点来显示存储库列表,请确保在此处和创建一个个人访问令牌将其附加在阿波罗标题中。

So Let’s get started.

因此,让我们开始吧。

首先,让我们构建我们的基本列表组件。 (First, let’s build our basic list component.)

For now, we’ll add just the basics in our list component: the repositories query, a flatlist, and a useQuery from apollo hooks to fetch the data.

现在,我们将仅在列表组件中添加基础知识:存储库查询,平面列表以及来自阿波罗钩子的Query,以获取数据。

import React from 'react'
import { Text, SafeAreaView, View, FlatList, ActivityIndicator } from 'react-native'
import { useQuery, gql } from '@apollo/client'
import RepositoryItem from './components/RepositoryItem'
import styles from './styles'


const REPOSITORIES_QUERY = gql`
 query($first: Int!) {
   organization(login: "facebook") {
     repositories(first: $first) {
       pageInfo {
         endCursor
         hasNextPage
       }
       nodes {
        id
         name
         updatedAt
         forkCount
         description
         url
       }
     }
   }
 }
`
const RepositoriesScreen = () => {
 const { data, error, loading } = useQuery(REPOSITORIES_QUERY, { variables: { first: 15 } })


 let repositories = []
 if (data && data.organization && data.organization.repositories)
   repositories = data.organization.repositories.nodes


 const renderItem = (props: any) => <RepositoryItem {...props} />
 // first time loading
 if (loading && repositories.length === 0)
   return (
     <View style={styles.loading}>
       <ActivityIndicator size="large" color="rgb(0, 122, 255)" />
     </View>
   )
 if (error)
   return (
     <View style={styles.loading}>
       <Text style={styles.errorText}>Something went wrong</Text>
     </View>
   )


 return (
   <SafeAreaView style={styles.container}>
     <FlatList
       data={repositories}
       renderItem={renderItem}
       keyExtractor={(item) => item.id}
       contentContainerStyle={{ flexGrow: 1 }}
     />
   </SafeAreaView>
 )
}


export default RepositoriesScreen

- Here we added a query to fetch the Facebook repositories with a first variable that will allow us to specify the number of the items being fetched.

-在这里,我们添加了一个查询来获取带有第一个变量的Facebook存储库,这将使我们能够指定要获取的项目数。

- the pageInfo in query contains two values(endcursor and hasNextPage) which we’ll use when fetching a new set of repo’s.

-查询中的pageInfo包含两个值(endcursor和hasNextPage),当我们获取一组新的存储库时将使用它们。

添加无限滚动 (Add The Infinite scroll)

To add the infinite scroll functionality we’ll use the fetchMore function from the useQuery result,

要添加无限滚动功能,我们将使用useQuery结果中的fetchMore函数,

This will allow us to make a new query with and merge the new result into the original result. You can read more about this function here.

这将使我们能够使用进行新查询,并将新结果合并为原始结果。 您可以在此处阅读有关此功能的更多信息。

const { data, error, loading, fetchMore } = useQuery(REPOSITORIES_QUERY, {
   variables: { first: 15 },
 })

Now let’s add the function that will handle the fetchMore

现在让我们添加将处理fetchMore的函数

const handleOnEndReached = () => {
   if (data.organization.repositories.pageInfo.hasNextPage)
     return fetchMore({
       variables: {
         after: data.organization.repositories.pageInfo.endCursor,
         first: 15,
       },
       updateQuery: onUpdate,
     })
 }
.
.
.


<FlatList
    ...
    onEndReachedThreshold={1}
    onEndReached={handleOnEndReached}
/>

Before we fetch more data I checked if there is more data available with `hasNextPage` which is a good thing to prevent unnecessary network requests (optional), then we passed the after and first variables, the after here refer to the endCursor(the last item fetched) this method is called cursor-based pagination you can read more about it here.

在获取更多数据之前,我检查了hasNextPage是否有更多数据可用,这对防止不必要的网络请求(可选)是一件好事,然后我们传递了after和first变量,after在这里引用了endCursor(最后提取此项目),该方法称为基于光标的分页,您可以在此处了解更多信息。

Now when the update will complete the onUpdate function will be fired, this function will be responsible for merging the previous data with the new one.

现在,当更新完成时,将触发onUpdate函数,该函数将负责将先前的数据与新的数据合并。

/// ...
 
const onUpdate = (prev, { fetchMoreResult }) => {
   if (!fetchMoreResult) return prev
   const { pageInfo } = fetchMoreResult.organization.repositories
   const nodes = [
     ...prev.organization.repositories.nodes,
     ...fetchMoreResult.organization.repositories.nodes,
   ]
   return Object.assign({}, prev, {
     organization: {
       __typename: prev.organization.__typename,
       repositories: {
         __typename: prev.organization.repositories.__typename,
         pageInfo,
         nodes,
       },
     },
   })
 }


// ...

Note that we inserted the new repo’s at the end of the list and we updated the pageInfo so we have its new values.

请注意,我们在列表的末尾插入了新的存储库,并更新了pageInfo,以便获得新的值。

拉刷新 (Pull Refresh)

In a lot of cases, we’ll need to implement the pull refresh along with the infinite scrolling, for that we’ll use the refetch function from the useQuery result.

在很多情况下,我们将需要实现拉刷新以及无限滚动,为此,我们将使用useQuery结果中的refetch函数。

import { NetworkStatus } from '@apollo/client'
.
.
.
const { data, error, loading, fetchMore, refetch, networkStatus } = useQuery(REPOSITORIES_QUERY, {
   variables: { first: 15 },
   notifyOnNetworkStatusChange: true,
 })

Here we also added the networkstatus property to identify the status of our query, and we set the notifyOnNetworkStatusChange option to true in order to trigger a rerender in our component when the refetch is called.

在这里,我们还添加了networkstatus属性以标识查询的状态,并且将notifyOnNetworkStatusChange选项设置为true,以便在调用refetch时触发组件中的重新渲染。

// add this
const refreshing = networkStatus === NetworkStatus.refetch
// prevent the loading indicator from appearing while refreshing
 if (loading && repositories.length === 0 && !refreshing)
   return (
     <View style={styles.loading}>
       <ActivityIndicator size="large" color="rgb(0, 122, 255)" />
     </View>
   )




// then add proper props to flatlist


<FlatList
  ...
  onRefresh={refetch}
  refreshing={refreshing} // to display the pull refresh indicator
  />

The refreshing constant will be set to true when the refetch function will be triggered, and notice how we prevented the first loading from rendering when refreshing is set to true.

当触发refetch函数时,刷新常数将设置为true,并且请注意,当刷新设置为true时,我们如何防止首次加载呈现。

感谢您阅读 (Thanks for reading )

→ You can find the source code in this repo.

→您可以在此仓库中找到源代码。

I hope you enjoyed reading this article, I would love to hear your thoughts and feedback in the comments below.

我希望您喜欢阅读本文,希望在下面的评论中听到您的想法和反馈。

Github | Twitter ✈️

Github | Twitter的✈️

翻译自: https://medium.com/swlh/easy-infinite-scrolling-with-apollo-hooks-in-react-native-8e1a4af46e3d

 类似资料: