Search code examples
next.jsapolloapollo-client

Apollo GraphQL loading query


I am using Apollo GraphQL to fetch posts to populate a next js project.

The code I currently have works but I would like to implement the loading state of useQuery instead of what I have below.

This is the index (Posts page if you will)

import Layout from "../components/Layout";
import Post from "../components/Post";
import client from "./../components/ApolloClient";
import gql from "graphql-tag";

const POSTS_QUERY = gql`
  query {
    posts {
      nodes {
        title
        slug
        postId
        featuredImage {
          sourceUrl
        }
      }
    }
  }
`;

const Index = props => {
  const { posts } = props;

  return (
    <Layout>
      <div className="container">
        <div className="grid">
          {posts.length
            ? posts.map(post => <Post key={post.postId} post={post} />)
            : ""}
        </div>
      </div>
    </Layout>
  );
};

Index.getInitialProps = async () => {
  const result = await client.query({ query: POSTS_QUERY });

  return {
    posts: result.data.posts.nodes
  };
};

export default Index;

Then it fetches an individual Post

    import Layout from "../components/Layout";
import { withRouter } from "next/router";
import client from "../components/ApolloClient";

import POST_BY_ID_QUERY from "../queries/post-by-id";

const Post = withRouter(props => {
  const { post } = props;

  return (
    <Layout>
      {post ? (
        <div className="container">
          <div className="post">
            <div className="media">
              <img src={post.featuredImage.sourceUrl} alt={post.title} />
            </div>
            <h2>{post.title}</h2>
          </div>
        </div>
      ) : (
        ""
      )}
    </Layout>
  );
});

Post.getInitialProps = async function(context) {
  let {
    query: { slug }
  } = context;
  const id = slug ? parseInt(slug.split("-").pop()) : context.query.id;
  const res = await client.query({
    query: POST_BY_ID_QUERY,
    variables: { id }
  });

  return {
    post: res.data.post
  };
};

export default Post;

Whenever I have tried to use the useQuery with '@apollo/react-hooks' I always end up with a data.posts.map is not a function.


Solution

  • When you use useQuery, you should not destructure the data like result.data.posts.nodes because the data field will be undefined and loading is true for the first time calling. That why you got an error data.posts.map is not a function. Then if your query fetch data successfully, the loading field will be false.
    You can try it:

    import Layout from "../components/Layout";
    import Post from "../components/Post";
    import client from "./../components/ApolloClient";
    import { useQuery } from "@apollo/react-hooks"
    import gql from "graphql-tag";
    
    const POSTS_QUERY = gql`
      query {
        posts {
          nodes {
            title
            slug
            postId
            featuredImage {
              sourceUrl
            }
          }
        }
      }
    `;
    
    const Index = props => {
      const { posts } = props;
      const { loading, error, data } = useQuery(POSTS_QUERY);
      
      if (loading) return <div>Loading...</div>
      if (error) return <div>Error</div>
      
      return (
        <Layout>
          <div className="container">
            <div className="grid">
              {data.posts.nodes.length
                ? data.posts.nodes.map(post => <Post key={post.postId} post={post} />)
                : ""}
            </div>
          </div>
        </Layout>
      );
    };