Apollo subscription, add incoming object into a nested array & in cache

Viewed 185

Apollo client 3.5.5

Query that brings paginated comments:

  const {
    data: comments,
    loading: commentsLoading,
    fetchMore,
    refetch,
    subscribeToMore
  } = useGetCommentsQuery({
    variables: {
      post_id: postId,
      order_by: "desc",
      limit: 40,
      offset: 0
    },
    fetchPolicy: "cache-and-network"
  });

GetCommentsQuery:

query GetComments(
  $post_id: uuid
  $limit: Int = 30
  $offset: Int = 0
  $order_by: order_by! = desc
) {
  comment(
    order_by: { created_at: $order_by }
    where: { post: { id: { _eq: $post_id } }, parent_comment_id: { _is_null: true } }
    limit: $limit
    offset: $offset
  ) {
    id
    author_id
    comment_liked
    content
    created_at
    firebase_id
    parent_comment_id
    post_id
    post {
      id
    }
    author_profile {
      id
      is_staff
      first_name
      latest_login
      profile_images(order_by: { order: asc }, limit: 1) {
        ...profileImages
      }
    }
    comment_images {
      ...commentImages
    }
    comment_likes_aggregate {
      aggregate {
        count
      }
      nodes {
        id
        comment_id
        profile_id
      }
    }
    child_comments(order_by: { created_at: asc }) {
      ...childComment
    }
  }
}

Listener:

useEffect(() => {
    subscribeToMore({
      document: OnCommentAddedDocument,
      variables: { post_id: postId, author_id: currentUser().uid },
      updateQuery: (prev, { subscriptionData }) => {
        if (Object.keys(prev || {}).length === 0) {
          return null;
        }

        if (!subscriptionData.data || subscriptionData?.data?.comment?.length === 0) {
          return prev;
        }

        const newComment = subscriptionData.data.comment[0];

        const exists = findItemNested(prev.comment, newComment?.id, "child_comments");

        if (exists) {
          return prev;
        }

        return Object.assign({}, prev, {
          comment: [...(prev.comment || []), newComment]
        });
      }
    });
   
  }, [postId]);

OnCommentAdded subscription:

  subscription OnCommentAdded(
    $post_id: uuid!
    $order_by: order_by! = desc
    $author_id: String!
  ) {
    comment(
      where: { post_id: { _eq: $post_id }, _not: { author_id: { _eq: $author_id } } }
      order_by: { created_at: $order_by }
      limit: 1
    ) {
      id
      author_id
      comment_liked
      content
      created_at
      firebase_id
      parent_comment_id
      post_id
      post {
        id
      }
      comment_images {
        ...commentImages
      }
      author_profile {
        id
        is_staff
        first_name
        latest_login
        profile_images(order_by: { order: asc }, limit: 1) {
          ...profileImages
        }
      }
      child_comments(order_by: { created_at: asc }) {
        ...childComment
      }
      comment_likes_aggregate {
        aggregate {
          count
        }
        nodes {
          id
          comment_id
          profile_id
        }
      }
    }
  }

child_comments fragment:

fragment childComment on comment {
  id
  author_id
  comment_liked
  content
  created_at
  firebase_id
  parent_comment_id
  post_id
  post {
    id
  }
  author_profile {
    id
    is_staff
    first_name
    latest_login
    profile_images(order_by: { order: asc }, limit: 1) {
      ...profileImages
    }
  }
  comment_images {
    ...commentImages
  }
  comment_likes_aggregate {
    aggregate {
      count
    }
    nodes {
      id
      comment_id
      profile_id
    }
  }
  child_comments(order_by: { created_at: asc }) {
    id
    author_id
    parent_comment_id
    post {
      id
    }
    post_id
    firebase_id
    comment_liked
    created_at
    comment_images {
      ...commentImages
    }
    author_profile {
      id
      is_staff
      first_name
      latest_login
      profile_images(order_by: { order: asc }, limit: 1) {
        ...profileImages
      }
    }
    content
    comment_likes_aggregate {
      aggregate {
        count
      }
      nodes {
        id
        comment_id
        profile_id
      }
    }
  }
}

cache setup:

new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        comment: offsetLimitPagination(["where"])
      }
    }
  }
});

If the comment added from another user does not have a parent_id then the comment is being added on the comments list just fine. When a comment with a parent_id coming in, it's being added under the parent as it should but it's being added at the end of the list too.

Is there a way to update the cache without "touching" the ROOT_QUERY? Do i have to recursively iterate on every single comment child_comments (and it's child_comments, 2 levels deep) and inject the new comment on the appropriate parent comment child_comments instead of doing:

return Object.assign({}, prev, {
        comment: [...(prev.comment || []), newComment]
 });

Comments could be hundreds, iterating over won't be efficient and I was thinking if there's a better way or there's something totally wrong with my queries/cache.

0 Answers
Related