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

时间:2021-07-26 13:42:32

标签: reactjs caching graphql apollo

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

我要找的结果是:

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

这是我的容器

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>
    )
}

和帖子列表

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 page{page}</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 的帖子等等。

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

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

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

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

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

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

1 个答案:

答案 0 :(得分: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 page{page}</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。您的问题是您目前正在混合使用这两种方法,这可能会导致奇怪的结果。