【问题标题】:Using fetchMore to fetch ALL data on component mount使用 fetchMore 获取组件挂载的所有数据
【发布时间】:2020-04-13 18:07:43
【问题描述】:

我有一种情况需要获取,例如安装组件时用户发布的所有文章。要获取用户的文章,我使用以下查询:

const GET_USER_ARTICLES = gql`
    query getUserArticles($id: ID, $numArticles: Int!, $cursor: String) {
        user(id: $id) {
            id
            articles(first: $numArticles, after: $cursor, orderBy: "-created", state: "enabled") @connection(key: "userArticles") {
                edges {
                    node {
                        name
                    }
                }
                pageInfo {
                    endCursor
                    hasNextPage
                }
            }
        }
    }
`;

如果有下一页,我想继续获取更多文章,直到我拥有所有文章。到目前为止,我还没有需要做这样的事情(通常我有一个按钮,用户可以点击“加载更多”来获取更多文章,但现在需要在没有用户与任何东西交互的情况下获取所有内容),所以我不确定最好的方法是什么。

React 中的查询示例:

const PAGE_SIZE = 10;

const { data, loading, fetchMore } = useQuery<UserArticlesData, UserArticlesVariables>(
    GET_USER_ARTICLES,
    { variables: { id: userId, numArticles: PAGE_SIZE, cursor: null } },
);

我有点迷失如何使用fetchMore 继续获取直到没有更多页面,同时还向用户显示加载状态。我也不确定这是否是解决此问题的最佳方式,因此欢迎提出任何建议!

【问题讨论】:

    标签: reactjs graphql apollo react-apollo apollo-client


    【解决方案1】:

    如果 API 不限制页面大小,您可以提供一个任意大的数字作为页面大小来获得剩余的结果。但是,假设页面大小只能这么大,您可以执行以下操作:

    const { data, loading, fetchMore } = useQuery(GET_USER_ARTICLES, {
      variables: { id: userId, numArticles: PAGE_SIZE, cursor: null },
      notifyOnNetworkStatusChange: true,
    })
    const fetchRest = async () => {
      const { user: { articles: { pageInfo } } } = data
      const updateQuery = (prev, { fetchMoreResult }) => {
        // Merge the fetchMoreResult and return the combined result
      }
    
      let hasNextPage = pageInfo.hasNextPage
      let cursor = pageInfo. endCursor
    
      while (hasNextPage) {
        const { data } = await fetchMore({
          variables: { id: userId, numArticles: PAGE_SIZE, cursor },
          updateQuery,
        })
        const { user: { articles: { pageInfo } } } = data
        hasNextPage = pageInfo.hasNextPage
        cursor = pageInfo. endCursor
      }
    }
    

    通过将notifyOnNetworkStatusChange 设置为trueloading 将在fetchMore 进行任何获取时更新。然后我们循环直到调用hasNextPagefetchMore 返回一个解析为查询结果的 Promise,因此我们可以在 updateQuery 函数之外使用查询响应。

    请注意,这是一个粗略的示例——例如,您实际上可能希望自己跟踪加载状态。如果你的 API 有速率限制,你的逻辑也应该考虑到这一点。不过,希望这可以为您提供一个良好的起点。

    编辑:

    如果您最初需要获取所有文章,我根本不会使用useQueryfetchMore。最简单的解决方法是自己管理数据和加载状态,并改用client.query

    const client = useApolloClient()
    const [data, setData] = useState()
    const [loading, setLoading] = useState(true)
    const fetchAll = async () => {
      let hasNextPage = true
      let cursor = null
      let allResults = null
    
      while (hasNextPage) {
        const { data } = await client.query(GET_USER_ARTICLES, {
          variables: { id: userId, numArticles: PAGE_SIZE, cursor },
        })
    
        // merge data with allResults
    
        hasNextPage = pageInfo.hasNextPage
        cursor = pageInfo. endCursor
      }
      setLoading(false)
      setData(allResults)
    }
    
    useEffect(() => {
      fetchAll()
    }, [])
    

    【讨论】:

    • 我已经让这个工作了,我唯一遇到的问题是它被多次调用,导致文章数量大于现实。我似乎无法找到解决方案——我尝试过使用useCallbackuseEffect,但它仍在发生。有什么想法吗?
    • 不确定你的意思——什么被多次调用?根据 OP,听起来您需要一个函数来触发以响应用户操作(如点击)。如果您尝试最初获取所有文章,上述方法将不起作用。
    • 对不起,这是我的错误:我的意思是当用户单击按钮时我通常使用fetchMore 来获取更多数据,但现在需要获取挂载上的所有内容。我将编辑 OP 以使其更清晰。无论哪种方式,您的答案正是我所需要的!
    • 我推荐 fetchMore 方法在任何情况下你想保留缓存以支持在其他地方修改缓存的突变。
    猜你喜欢
    • 2021-04-25
    • 2021-12-08
    • 2019-02-14
    • 2017-08-09
    • 2020-01-23
    • 2020-10-14
    • 2022-01-16
    • 2023-03-25
    • 2018-01-10
    相关资源
    最近更新 更多