Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apollo js Subscriptions Optimistic UI

Does it make sense having Optimistic UI with subscriptions?

So basically:

addChannelMutation({
  variables: { name: eventValue },
  optimisticResponse: {
    __typename: "Mutation",
    addChannel: {
      __typename: "Channel",
      id: data.channels.length,
      name: eventValue
    }
  },
  update: (store, { data: { addChannel } }) => {
    // Read the data from our cache for this query.
    const data = store.readQuery({ query: channelsListQuery });
    // Add our comment from the mutation to the end.
    data.channels.push(addChannel);
    // Write our data back to the cache.
    store.writeQuery({ query: channelsListQuery, data });
  }
}).then(res => {});

It adds twice the same item triggering a duplicate key exception. Thus does optimistic ui make sense with subscriptions?

like image 705
Luca Marangon Avatar asked Mar 10 '23 01:03

Luca Marangon


1 Answers

optimisticResponse triggers update before a response is received by the server. Then when the server responds, update is triggered again and replaces the optimistic placeholder with the response.

Subscriptions will emit only once the server mutation resolves, so essentially when the server is responding.

If you didn't include Optimistic UI and you had any sort of latency, the result won't show up until the server sends a response. This can be an issue, for example in a chat app, if the user doesn't see their message as soon as they click the send button. They'll keep clicking the button and send the message multiple times :/

To fight dupes when using Optimisic UI and Subscriptions, two strategies include:

  1. check for dupes on the client:

    in the update and updateQuery functions:

    // super simplified dupe doc checker
    function isDuplicateDocument(newDocument, existingDocuments) {
      return newDocument.id !== null && existingDocuments.some(doc => newDocument.id === doc.id);
    }
    
    addChannelMutation({
      variables: { name: eventValue },
      optimisticResponse: {
        __typename: "Mutation",
        addChannel: {
          __typename: "Channel",
          id: data.channels.length,
          name: eventValue
        }
      },
      update: (store, { data: { addChannel } }) => {
        // Read the data from our cache for this query.
        const data = store.readQuery({ query: channelsListQuery });
        if (isDuplicateDocument(addChannel, data.channels) {
          return;
        }
    
        // Add our comment from the mutation to the end.
        data.channels.push(addChannel);
        // Write our data back to the cache.
        store.writeQuery({ query: channelsListQuery, data });
      }
    }).then(res => {});
    

    And also inside updateQuery in your subscription:

    subscribeToMore({
      ...
      updateQuery: (previousResult, { subscriptionData }) => {
        const newChannel = subscriptionData.data.addChannel;
        // if it's our own mutation
        // we might get the subscription result
        // after the mutation result.
        if (isDuplicateDocument(
          newChannel, previousResult.channels)
        ) {
          return previousResult;
        }
    
        return update(previousResult, {
          channels: {
            $push: [newChannel],
          },
        });
      },
    
  2. Or you can limit your subscription on the server to not emit to the creator of the new channel.

like image 79
srtucker22 Avatar answered Mar 25 '23 09:03

srtucker22