So, I just wanted to make sure I was doing this correctly because it doesn’t feel right.
What I want:
- When I click the button to fetchMore, it automatically updates my page to show the new results.
What I have:
getPosts.ts
import { gql } from '@apollo/client'
export const GET_POSTS = gql`
query GetPosts(
$after: String
$filterBy: [PostListFilterByInput!]
$limit: Int!
$offset: Int
$orderByString: String
$reverse: Boolean
$spaceIds: [ID!]
) {
posts(
after: $after
filterBy: $filterBy
limit: $limit
offset: $offset
orderByString: $orderByString
reverse: $reverse
spaceIds: $spaceIds
) {
totalCount
pageInfo {
hasNextPage
endCursor
}
edges {
node {
id
title
description
reactionsCount
fields {
key
value
relationEntities {
medias {
... on Image {
urls {
medium
}
}
}
}
}
}
}
}
}
`
usePosts.ts
hook
import { useQuery } from '@apollo/client'
import {
GetPostsQuery,
GetPostsQueryVariables,
Image
} from '@/graphql/generated/types'
import { GRAPHQL_VARIABLES } from '@/constants/graphql'
import { GET_POSTS } from '@/graphql/queries/posts/getPosts'
export interface IPost {
id: string
title: string
description: string
reactionsCount: number
urls: {
medium?: string
}
}
export const useGetPosts = () => {
const { data, loading, error, fetchMore } = useQuery<
GetPostsQuery,
GetPostsQueryVariables
>(GET_POSTS, {
variables: {
...GRAPHQL_VARIABLES,
after: ''
}
})
const posts: IPost[] =
data?.posts?.edges?.map(({ node }) => {
let urls = {}
if (node.fields) {
for (const field of node.fields) {
if (field.key === 'cover_image' && field.relationEntities?.medias) {
const image = field.relationEntities.medias.find(
(media) => media.__typename === 'Image'
) as Image
if (image) {
urls = image.urls || {}
}
}
}
}
return {
id: node.id || '',
title: node.title || '',
description: node.description || '',
reactionsCount: node.reactionsCount,
urls
}
}) || []
const hasNextPage = data?.posts?.pageInfo?.hasNextPage
const endCursor = data?.posts?.pageInfo?.endCursor
return { posts, loading, error, fetchMore, hasNextPage, endCursor }
}
Gallery.tsx
component
import GalleryItem from '@/components/feature/GalleryItem'
import { Button } from '@/components/base/Button'
import { useGetPosts } from './hooks/usePosts'
const Gallery: React.FC = () => {
const { posts, fetchMore, hasNextPage, endCursor } = useGetPosts()
const loadMore = () => {
fetchMore({
variables: {
after: endCursor
}
})
}
return (
<>
<div className="flex justify-center">
<div className="m-8 grid max-w-[1100px] grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
{posts.map((post) => (
<GalleryItem key={post?.id} post={post} />
))}
</div>
</div>
<div className="mt-10 flex justify-center">
{hasNextPage && (
<Button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
onClick={loadMore}
>
Show More
</Button>
)}
</div>
</>
)
}
export default Gallery
cache.ts
graphql cache config:
import { InMemoryCache } from '@apollo/client'
export const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
posts: {
keyArgs: false,
merge(existing = { edges: [], pageInfo: {} }, incoming) {
const mergedEdges = [...existing.edges, ...incoming.edges]
return {
...incoming,
edges: mergedEdges
}
}
}
}
}
}
})
Is this correct or is there a better way to merge data after doing a fetchMore request?