Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inserted query data from optimistic response update persists in the cache #8294

Open
jaril opened this issue May 26, 2021 · 4 comments
Open
Labels

Comments

@jaril
Copy link

jaril commented May 26, 2021

I'm currently working on a commenting UI for an application. Ideally, new comments appear immediately as they're refetched from the server once the mutation is completed. In reality, it ends up taking longer and there's a 2-3 second delay between submitting a new comment and seeing it on the UI.

To fix this, I've been using an optimisticResponse, which works great to update the UI immediately. However, things start breaking when I use it alongside a pollInterval. To be more specific, I'd like the comments to refresh every 5 seconds to keep it fresh.

Intended outcome:

There's a getComments query for getting all the comments. The following events should happen as a user adds a new comment:

  1. The addComment mutation should update the cache with the optimisticResponse by inserting the optimistic comment data into the getComments query with writeQuery().
  2. The cached data with the optimistic comment data should immediately update the UI.
  3. When the real server response comes back, we should disregard the optimistic comment data, and insert the data coming from the server.
  4. The cached data with the real data should immediately update the UI.

This set of steps happen ~90% of the time. The 10% case is documented as the actual outcome below.

Actual outcome:

  1. The addComment mutation updates the cache with the optimisticResponse by inserting the optimistic comment data into the getComments query with writeQuery().
  2. The cached data with the optimistic comment data immediately updates the UI.
  3. The real server response comes back.
  4. When I check the data for the getComments query, it includes the optimistic comment data.

#4 is the problem, as this leads to duplicated comments in the query. This appears to happen when we enable polling every 5s to refresh the query data.

My working theory is that in usual cases, optimistic responses are earmarked as optimistic in the cache. That's why they're not shown when checking that same query in the update function with the real server data.

This expected behavior changes when you have polling in-flight. I've tried testing it further, but I'm limited in testing the inner workings of Apollo. As far as I can tell, it looks like something is causing the cached optimistic data to no longer be earmarked as optimistic, which is why it ends up appearing in the query when I don't expect it to. Two cases I thought might lead to this are:

  1. When a fetch triggered by the polling is kicked off.
  2. When a fetch triggered by the polling comes back with new data.

Is this expected behavior for apollo-client? Is there some way to make sure that the actual outcomes behave like the intended outcome instead?

Code

export const GET_COMMENTS = gql`
  query GetComments($recordingId: UUID!) {
    recording(uuid: $recordingId) {
      uuid
      comments {
        id
        content
        createdAt
        updatedAt
        hasFrames
        sourceLocation
        time
        point
        position
        user {
          id
          name
          picture
        }
        replies {
          id
          content
          createdAt
          updatedAt
          user {
            id
            name
            picture
          }
        }
      }
    }
  }
`;

export function useGetComments(
  recordingId: RecordingId
): { comments: Comment[]; loading: boolean; error?: ApolloError } {
  const { data, loading, error } = useQuery(GET_COMMENTS, {
    variables: { recordingId },
    pollInterval: 5000,
  });

  return { comments: data.comments, loading, error };
}

export default function useAddComment() {
  const { user } = useAuth0();

  const [addComment, { error }] = useMutation(
    gql`
      mutation AddComment($input: AddCommentInput!) {
        addComment(input: $input) {
          success
          comment {
            id
          }
        }
      }
    `
  );

  return (comment: NewCommentVariable, recordingId: RecordingId) => {
    addComment({
      variables: { input: comment },
      optimisticResponse: {
        addComment: {
          success: true,
          comment: {
            id: new Date().toISOString(),
            __typename: "Comment",
          },
          __typename: "AddComment",
        },
      },
      update: (cache, { data: { addComment } }) => {
        const {
          comment: { id: commentId },
        } = addComment;
        const data: any = cache.readQuery({
          query: GET_COMMENTS,
          variables: { recordingId },
        });
        const {
          viewer: {
            user: { id: userId },
          },
        }: any = cache.readQuery({
          query: GET_USER_ID,
        });

        const newComment = {
          ...comment,
          id: commentId,
          replies: [],
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          user: {
            id: userId,
            name: user.name,
            picture: user.picture,
            __typename: "User",
          },
        };
        const newData = {
          ...data,
          recording: {
            ...data.recording,
            comments: [...data.recording.comments, newComment],
          },
        };

        cache.writeQuery({
          query: GET_COMMENTS,
          variables: { recordingId },
          data: newData,
        });
      },
    });
  };
}

How to reproduce the issue:

Versions

  System:
    OS: macOS 10.15.7
  Binaries:
    Node: 14.16.1 - /usr/local/bin/node
    Yarn: 1.17.0 - /usr/local/bin/yarn
    npm: 6.14.12 - /usr/local/bin/npm
  Browsers:
    Chrome: 90.0.4430.212
    Firefox: 88.0
    Safari: 14.1
  npmPackages:
    @apollo/client: ^3.1.5 => 3.2.0 
    apollo-link-http: ^1.5.17 => 1.5.17 
@benjamn
Copy link
Member

benjamn commented May 26, 2021

@jaril What version of @apollo/client (and any related packages) are you using?

@jaril
Copy link
Author

jaril commented May 26, 2021

@jaril What version of @apollo/client (and any related packages) are you using?

Missed that field — here you go!

  System:
    OS: macOS 10.15.7
  Binaries:
    Node: 14.16.1 - /usr/local/bin/node
    Yarn: 1.17.0 - /usr/local/bin/yarn
    npm: 6.14.12 - /usr/local/bin/npm
  Browsers:
    Chrome: 90.0.4430.212
    Firefox: 88.0
    Safari: 14.1
  npmPackages:
    @apollo/client: ^3.1.5 => 3.2.0 
    apollo-link-http: ^1.5.17 => 1.5.17 

@benjamn
Copy link
Member

benjamn commented May 26, 2021

For diagnostic purposes, could you try updating to @apollo/client@3.4.0-rc.2 (the latest beta release of AC3.4)?

@jaril
Copy link
Author

jaril commented May 26, 2021

For diagnostic purposes, could you try updating to @apollo/client@3.4.0-rc.2 (the latest beta release of AC3.4)?

Sure! Just tried it and the same problem persists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants