apollo-client: [v3] cache.modify with optimistic result does not broadcast change

I have a schema where a Taxonomy is a self referencing node, in a one-to-many parent child relationship:

type Taxonomy {  
  id: ID!
  title: String! @search(by: [fulltext])
  parent: Taxonomy
  children: [Taxonomy] @hasInverse(field: parent)
}

The @hasInverse directive is dgraph-specific, and sets up a two-way relationship, that updated the inverse edge when one is modified.

I am attempting to optimistically add a Taxonomy node under a parent in the following mutation:

mutation AddTaxonomy ($title: String!, $parentId: ID!) {

    addTaxonomy(input: [{
        title: $title,
        parent: {
            id: $parentId
        }
    }]) {
        taxonomy {
            id,
            title,
            parent {
                id
            }            
        }
    }
}

I want the UI to update optimistically, inserting a temporary object until the query is completed, so I specify a compatible optimistic result.

Apollo doesn’t know anything about the @hasInverse directive, so I create options that provide an update function to modify the cache and keep the parent’s children up-to-date in each case.


    const id = getTmpId();    

    const options = {
        variables: {
            title,
            parentId: parentId
        },
        optimisticResponse: {
            __typename: "Mutation",
            addTaxonomy: {
                __typename: "AddTaxonomyPayload",
                taxonomy: [{
                    __typename: "Taxonomy",
                    id,
                    title: "TEMP",
                    parent: {
                        __typename: "Taxonomy",
                        id: parentId
                    }
                }]
            }
        },
        update: (cache, { data: { addTaxonomy } }) => {
            const added = addTaxonomy.taxonomy[0];
            const parent = added.parent;

            cache.modify({
                id: cache.identify(parent),
                fields: {
                    children: (existing: Reference[]) => {   
                        const newTaxonomy = cache.writeFragment({
                            data: added,
                            fragment: gql`
                              fragment NewTaxonomy on Taxonomy {
                                id
                                title
                                parent {
                                    id
                                }
                              }
                            `
                          });
                        return [...existing, newTaxonomy]
                    }
                }
            });
            
        }
    }

Both results (optimistic and actual) reach the update function. Both results are the same shape. Only the actual result from the query emits in a change in the query data shown in my UI. The temporary Taxonomy is nowhere to be seen.

I might be missing something, but it sure seems like a bug to me.

More, after I delete and add the node a few times in succession, eventually the add fails silently, and the result data from the add payload gets emitted to the UI as ‘undefined’.

Not sure what’s going on there…

Is AC3 still ironing out bugs with this kind of stuff?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (8 by maintainers)

Most upvoted comments

writeFragment should throw an error in that case. @benjamn Cool if I assign myself to tackle that?

@jcreighton Go for it (in a new PR please)!

@martaver Thank you for the updated repro link! I’ve made some modifications and it’s working, check it out: https://codesandbox.io/s/green-cdn-hef5f?file=/src/App.js

These are the changes:

  • Add typename to the parent object in both App.js (line 39) and link.js (line 56). This is required for cache.identify (App.js, line 49) to create the correct ref.
  • Send the correct data to writeFragment (App.js, line 53). Previous to this, the data object referenced here was the data from the AllPeople query. You can see this on line 47 where I’m logging out the data. This meant that writeFragment was unable to identify the correct ref for the child.
  • Changed “title” to “name” (App.js, lines 38 & 57, Link.js, line 55). Without this change, the UI didn’t render the new child.

This was tricky to resolve and would’ve been easier to catch if writeFragment didn’t return undefined when it can’t identify the object. writeFragment should throw an error in that case. @benjamn Cool if I assign myself to tackle that? (And should that be a new issue or keep it as part of this one?)

@martaver I’ve been able to resolve the issue with the above CodeSandbox repros here. Press the Add friend button to run the mutation with an optimistic result. I see the optimistic result in the UI but there’s a chance I didn’t re-implement this as close to your use case above. If so, please feel free to modify it!