带有 React 的 ApolloClient V3:缓存和重用 fetchMore 查询的结果

Posted

技术标签:

【中文标题】带有 React 的 ApolloClient V3:缓存和重用 fetchMore 查询的结果【英文标题】:ApolloClient V3 with React: Caching and re-using result of fetchMore query 【发布时间】:2021-10-02 10:59:40 【问题描述】:

我有一个使用 React、TS 和 Apollo 构建的小型学习项目。 作为后端,我使用https://graphqlzero.almansi.me/

我要找的结果是:

    按页获取帖子,每页 5 个 重复使用之前获取的数据

这是我的容器

export const GET_POSTS = gql`
 
    query GetPosts($options: PageQueryOptions!) 
        posts(options: $options) 
            data 
                id
                title
                body
            
        
    
`


const Posts = (): JSX.Element => 
    const [page, setPage] = useState<number>(1)
    const  data, fetchMore = useQuery(GET_POSTS, 
        variables: 
            options: 
                paginate: 
                    page: 1,
                    limit: 5,
                ,
            ,
        ,
            nextFetchPolicy: "cache-first"
    ,
        )
    return (
        <div>
            <ListOfPosts ...data,fetchMore,page,setPage  />
        </div>
    )

和 ListOfPosts

const ListOfPosts = ( data,fetchMore, page, setPage ) => 
    const getNextPage = (): void => 
        fetchMore(
            variables: 
                options: 
                    paginate: 
                        page: page + 1,
                        limit: 5,
                    ,
                ,
            ,
        )
        setPage((p: number) => p + 1)
    

    const getPrevPage = (): void => 
        setPage((p: number) => (p === 0 ? p : p - 1))
    
    console.log(data)
    return (
        <div>
            <p>current pagepage</p>
            <button type="button" onClick=getPrevPage>
                Get Prev Page
            </button>
            <button type="button" onClick=getNextPage>
                Get Next Page
            </button>
            data &&
                data?.posts?.data?.map((post: any) => (
                    <div key=post.id>
                        <h4>post.title</h4>
                        <p>post.body</p>
                    </div>
                ))
        </div>
    )

因此,如果我发送一个带有 page === 1 的查询,我会收到从 1 到 5 的帖子,如果 page === 2 - 从 6 到 10 的帖子等等。

问题是,例如,如果我在下一个序列中发送请求

    page === 1(最初由 useQuery 发送) page === 2(使用 fetchMore 发送) page === 3(使用 fetchMore 发送) page === 2(使用 fetchMore 发送)

在最后一个请求上,Apollo 执行网络请求,尽管该请求的数据已经在缓存中 所以我的问题实际上是:

    如何配置 Apollo 缓存以返回所需数据而不从服务器重新获取数据 如何使该数据“无效”并告诉 Apollo 我需要刷新该特定数据部分?

我认为它应该以某种方式在缓存 typePolicies 中进行配置,但尚未找到使其工作的方法 - 尽管数据在缓存中(我可以使用浏览器扩展程序跟踪它),但它不会在 data=useQuery 中返回: / 这是我的缓存配置的样子。

export const cache = new InMemoryCache(
    typePolicies: 
        Query: 
            fields: 
                posts: 
                    merge(existing, incoming) 
                        return [ ...existing,...incoming ]
                    
                
            
        
    
)

【问题讨论】:

【参考方案1】:

所以有两种类型的分页。在第一个分页中,“页面”以某种方式为用户所知,用户通过 UI 浏览这些页面。这似乎是你想要做的:

我建议保持简单:更改状态变量,将它们传递到useQuery 并呈现结果。每次变量更改时,Apollo 都会使用新的页面值再次运行查询。如果 Apollo 之前看到过相同的变量(您返回上一页),Apollo 可以从缓存中返回结果。

export const GET_POSTS = gql`
    query GetPosts($options: PageQueryOptions!) 
        posts(options: $options) 
            data 
                id
                title
                body
            
        
    
`


const Posts = (): JSX.Element => 
    const [page, setPage] = useState<number>(1)
    const  data  = useQuery(GET_POSTS, 
        variables: 
            options: 
                paginate: 
                    page,
                    limit: 5,
                ,
            ,
        ,
        nextFetchPolicy: "cache-first"
    )

    return (
        <div>
            <ListOfPosts ...data,page,setPage  />
        </div>
    )


const ListOfPosts = ( data, page, setPage ) => 
    const getNextPage = (): void => setPage((p: number) => p + 1);
    const getPrevPage = (): void =>
        setPage((p: number) => (p === 0 ? p : p - 1))

    return (
        <div>
            <p>current pagepage</p>
            <button type="button" onClick=getPrevPage>
                Get Prev Page
            </button>
            <button type="button" onClick=getNextPage>
                Get Next Page
            </button>
            data &&
                data?.posts?.data?.map((post: any) => (
                    <div key=post.id>
                        <h4>post.title</h4>
                        <p>post.body</p>
                    </div>
                ))
        </div>
    )

仅当您想要一个接一个地连续显示结果时,才需要合并缓存。诸如“加载更多”或“无限滚动”之类的东西。这意味着您要继续添加到结果列表中。您将向视图中添加越来越多的帖子,并且旧页面不会消失。这就是 fetchMore 的设计目的。如果您想这样做,请查看文档和this part specifically。您的问题是您目前正在混合使用这两种方法,这可能会导致奇怪的结果。

【讨论】:

以上是关于带有 React 的 ApolloClient V3:缓存和重用 fetchMore 查询的结果的主要内容,如果未能解决你的问题,请参考以下文章

React 和 ApolloClient:网络错误:forward(...).subscribe 不是函数

将 mutate() 从 ApolloClient 移动到 React 组件的正确方法?

apolloClient.query 不使用中间件,而 <Query /> 使用

带有 React 的 Apollo 客户端 2 无法进行查询

通过 react(ApolloClient.query()) 方法从 GraphQL 服务器获取数据

无法处理 React ApolloClient 中的错误