HeyImArt
HeyImArt

Reputation: 538

Apollo GraphQL FetchMore

I'm trying to get Apollo gql to load more posts after clicking a button. So it would load the next 15 results, every time you click - load more.

This is my current code

import Layout from "./Layout";
import Post from "./Post";
import client from "./ApolloClient";
import { useQuery } from "@apollo/react-hooks"
import gql from "graphql-tag";

const POSTS_QUERY = gql`
  query {
    posts(first: 15) {
      nodes {
        title
        slug
        postId
        featuredImage {
          sourceUrl
        }
      }
    }
  }
`;

const Posts = props => {
  let currPage = 0;
  const { posts } = props;
  const { loading, error, data, fetchMore } = useQuery(
    POSTS_QUERY,
    {
      variables: {
        offset: 0,
        limit: 15
      },
      fetchPolicy: "cache-and-network"
    });

  function onLoadMore() {
    fetchMore({
      variables: {
        offset: data.posts.length
      },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (!fetchMoreResult) return prev;
        return Object.assign({}, prev, {
          posts: [...prev.posts, ...fetchMoreResult.posts]
        });
      }
    });
  }

  if (loading) return (
      <div className="container mx-auto py-6">
        <div className="flex flex-wrap">
          Loading...
        </div>
      </div>
  );
  if (error) return (
      <div className="container mx-auto py-6">
        <div className="flex flex-wrap">
          Oops, there was an error :( Please try again later.
        </div>
      </div>
  );
  return (
      <div className="container mx-auto py-6">
        <div className="flex flex-wrap">
          {data.posts.nodes.length
            ? data.posts.nodes.map(post => <Post key={post.postId} post={post} />)
            : ""}
        </div>

        <button onClick={() => { onLoadMore() }}>Load More</button>
      </div>
  );
};

export default Posts;

When you click load more it refreshes the query and console errors Invalid attempt to spread non-iterable instance

I have been loading for solutions but a lot of the examples are previous or next pages like traditional pagination. Or a cursor based infinite loader which I don't want. I just want more posts added to the list onClick.

Any advise is appreciated, thank you.

Upvotes: 2

Views: 7473

Answers (2)

alexojegu
alexojegu

Reputation: 796

Your current POSTS_QUERY it isn't accepting variables, so first you need change this:

const POSTS_QUERY = gql`
  query postQuery($first: Int!, $offset: Int!) {
    posts(first: $first, offset: $offset) {
      nodes {
        title
        slug
        postId
        featuredImage {
          sourceUrl
        }
      }
    }
  }
`;

Now, it will use the variables listed in your useQuery and fetchMore.

And to finish the error is because updateQuery isn't correct, change it to:

function onLoadMore() {
    fetchMore({
      variables: {
        offset: data.posts.nodes.length
      },
      updateQuery: (prev, { fetchMoreResult }) => {
        if (!fetchMoreResult) return prev;
        return { posts: { nodes: [...prev.posts.nodes, ...fetchMoreResult.posts.nodes] } };
        });
      }
    });
  }

Upvotes: 4

Achal Jain
Achal Jain

Reputation: 379

I would suggest useState hook to manage a variable that stores current offset in the dataset, place a useEffect to watch changes to that offset, the offset value in passed as query variable to load data. Remove fetchmore, useEffect hook will do the job.

When user clicks on load more button, you just need to update offset value, that will trigger the query and update data.

const [offset,setOffset] = React.useState(0)
const [results, setResults] = React.useState([])

const { loading, error, data } = useQuery(
    POSTS_QUERY,
    {
      variables: {
        offset: offset,
        limit: 15
      },
      fetchPolicy: "cache-and-network"
    }
);

React.useEffect(() => {
 const newResults = [...results, ...data]
 setResults(newResults)
}, [data])

function onLoadMore() {
 setOffset(results.data.length)
}

Upvotes: 1

Related Questions