Apollo GraphQL Deconstruction of data not working

Viewed 2221

I am digging graphql so I followed a tutorial, And I stucked in this part.

Home.js

function Home() {
  const {
    loading,
    data: { getPosts: posts }   // <===## Here ##
  } = useQuery(FETCH_POSTS_QUERY);

  return (
      <div>
        {loading ? (
          <h1>Loading posts..</h1>
        ) : (
          posts &&
          posts.map((post) => (
            <p>
              {post.content}
            </p>
          ))
        )}
      </div>
  );
}

const FETCH_POSTS_QUERY = gql`
  {
    getPosts {
      id
      content
    }
  }
`;


export default Home;

resolver

  Query: {
    async getPosts() {
      try {
        const posts = await Post.find().sort({ createdAt: -1 });
        return posts;
      } catch (err) {
        throw new Error(err);
      }
    }
  },

Whole code: https://github.com/hidjou/classsed-graphql-mern-apollo/tree/react10

In above example is working well, and it use it use data: { getPosts: posts } for deconstruction of returned data. but In my code, I followed it but I got an error

TypeError: Cannot read property 'getPosts' of undefined

Instead, If I code like below,

function Home() {
  const {
    loading,
    data // <===## Here ##
  } = useQuery(FETCH_POSTS_QUERY);

  if(loading) return <h1>Loading...</h1>

  const { getPosts: posts } = data // <===## Here ##

  return (
      <div>
        {loading ? (
          <h1>Loading posts..</h1>
        ) : (
          posts &&
          posts.map((post) => (
            <p>
              {post.content}
            </p>
          ))
        )}
      </div>
  );
}

It working well. Seems like my code try to reference data before it loaded. But I don't know why this happen. Code is almost same. Different things are 1. my code is on nextjs, 2. my code is on apollo-server-express. Other things are almost same, my resolver use async/await, and will return posts. Am I miss something?

my resolver is like below.

 Query: {
    async getPosts(_, { pageNum, searchQuery }) {
      try {
        const perPage = 5
        const posts =
          await Post
            .find(searchQuery ? { $or: search } : {})
            .sort('-_id')
            .limit(perPage)
            .skip((pageNum - 1) * perPage)
        return posts
      } catch (err) {
        throw new Error(err)
      }
    },
1 Answers

Your tutorial may be out of date. In older versions of Apollo Client, data was initially set to an empty object. This way, if your code accessed some property on it, it wouldn't blow up. While this was convenient, it also wasn't particularly accurate (there is no data, so why are we providing an object?). Now, data is simply undefined until your operation completes. This is why the latter code is working -- you don't access any properties on data until after loading is false, which means the query is done and data is no longer undefined.

If you want to destructure data when your hook is declared, you can utilize a default value like this:

const {
  loading,
  data: { getPosts: posts } = {}
} = useQuery(FETCH_POSTS_QUERY)

You could even assign a default value to posts as well if you like.

Just keep in mind two other things: One, data will remain undefined if a network error occurs, even after loading is changed to true, so make sure your code accounts for this scenario. Two, depending on your schema, if there's errors in your response, it's possible for your entire data object to end up null. In this case, you'll still hit an issue with destructuring because default values only work with undefined, not null.

Related