React-apollo: Allow partial optimistic responses in mutations

Created on 1 Jun 2018  ·  7Comments  ·  Source: apollographql/react-apollo

Assuming the following mutation:

const TOGGLE_MUTATION = gql`
  mutation ToggleMutation($input: ToggleInput!) {
    toggle(input: $input) {
      foo {
        id
        enabled
        updatedAt
      }
    }
  }
`;

This is pretty straightforward: as an input, send the id of the object to toggle, as well as a boolean that indicates if it should be enabled or disabled.
As a response, we get the id and state of enabled back, as well as the exact updatedAt timestamp at what time it was precisely updated.

This migration is run when checking/unchecking a checkbox:

<Mutation mutation={TOGGLE_MUTATION}>
  {toggle => (
    <input
      type="checkbox"
      checked={foo.enabled}
      onChange={async event => {
        const input = {
          id: foo.id,
          enabled: event.target.checked,
        };

        return toggle({
          variables: { input },
          optimisticResponse: optimisticToggle(input),
        });
      }}
    />
  )}
</Mutation>

When checking/unchecking, the migration gets passed the id of foo (a simple object outside this snippet) and the checked state of the checkbox as the enabled state.
When the response arrives, on top of the checkbox, we have a text on screen that says "Last updated at xxx".

To make the experience feel instantaneous, we use an optimisticResponse function. Without this, the checkbox actually doesn't change until the server comes back, which can take long enough to cause the user to click many times thinking it didn't work.

However, updatedAt cannot be optimistically updated. This means that its value is not updated instantly, and instead it is updated when the response comes back. This is the intended result, so that "Last updated at xxx" is always accurate, and the checkbox is always unsurprising. The optimisticResponse function to match this behavior is:

function optimisticToggle({ id, enabled }) {
  return {
    __typename: 'Mutation',
    toggle: {
      __typename: 'TogglePayload',
      foo: {
        __typename: 'Foo',
        id,
        enabled,
        // `updatedAt` cannot be optimistically updated
      },
    },
  };
}

However, when doing so, the following warning appears in the JS console when clicking on the checkbox:

Missing field updatedAt in {
  "__typename": "Foo",
  "id": "123456",
  "enabled[...]

I understand that the actual response from the server must always match the shape sent in the query per the GraphQL spec, but in many cases, not everything can be _optimistically_ updated and therefore some fields will only receive the actual server update.

It would be great if the returned value of optimisticResponse did not have the same constraints as the actual server response. I think we would be happy to help with this because this is a scenario we will encounter a lot.
I would love some pointers on how to start, and then I could open a PR about it if the proposal makes sense :)

Version

All 7 comments

if your field is nullable : in this case you can add null to your optimistic response. I agree that null should be added automatically for all the undefined fields. Maybe it's the case now

If your field is not nullable, what you propose is not possible. In this case, your store would be invalid, with a null/undefined field for a non nullable field, meaning that any connected component (especially if you use static type checking) would risk an error

If you need more flexible or granular control, look at using the mutation update option with either writeQuery or writeData, whichever best fits your scenario.

If you get something that could be more broadly used, then look at bringing it back generically to the Mutation here. I suspect it will be hard to make generic, but you never know until you try!

I'm going to close this as not actionable, but if you end up with a PR please feel free to ping me for a review.

I need to optimistically update a post uploaded by a user. I have everything to update optimistically except the post id, which is generated by the backend. Can I use the cache for this optimistic update?

Yes: “Tutorial: Speeding up GraphQL Mutations with optimistic UI” by Jonas
Helfer https://link.medium.com/LpyWA6K2QV

Put a negative id as optimistic

Antoine Sauvage
Phone/Whatsapp: +33 (0) 6 06 55 43 16

Le sam. 13 avr. 2019 à 15:10, Jai notifications@github.com a écrit :

I need to optimistically update a post uploaded by a user. I have
everything to update optimistically except the post id, which is generated
by the backend. Can I use the cache for this optimistic update?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/apollographql/react-apollo/issues/2063#issuecomment-482807884,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AGXbpcqNzDVAnWXY6WVikBoZEhWOyKRlks5vgddPgaJpZM4UXXIv
.

if your field is nullable : in this case you can add null to your optimistic response. I agree that null should be added automatically for all the undefined fields. Maybe it's the case now

I agree, it would be nice if the null fields were added automatically, seems like it's just clutter to add them manually. @rosskevin are you saying that I can get optimisticResponse-like behavior using the writeQuery or writeData options on update?

@rosskevin the problem here is that there is a need for _less_ granular control. Using update for this situation doesn't help anything, since update only gets called if you've already set optimisticResponse. This ends up being circular since optimisticResponse can't be partial, so you end up back at this issue to even use update!

This is really an issue if you have a circular dependency structure in your graphql where two fragments refer to each other. Say you have a books fragment with an author. This author is another fragment that can also reference books that the author has written. If I actually fill out the optimistic response with all of the correct values, then it will be a neverending dependency tree...

Was this page helpful?
0 / 5 - 0 ratings