When using mutate with optimistic UI, duplicate items are created, though the duplicate isn't really in the database (it's a temporary DOM dupe).
I can verify this. See code below:
graphql(MutationCreateChild, {
options: {
refetchQueries: [{ query: QueryAllChildren }],
update: (proxy, { data: { createChild } }) => {
console.log("item in update: ", createChild);
const query = QueryAllChildren;
const data = proxy.readQuery({ query });
data.listChildren.items.push(createChild);
proxy.writeQuery({ query, data });
}
},
props: props => ({
createChild: child => {
const fChild = graphqlFriendlyObject(child);
return props.mutate({
variables: fChild,
optimisticResponse: () => ({
createChild: {
...child,
id: "-" + uuid(),
created: moment().valueOf(),
version: "1",
__typename: "Child"
}
})
});
}
})
})
item in update:
{name: "Test Item", description: "", isActive: true, lastModified: "", created: 1520443086752, …}
created
:
1520443086752
description
:
""
id
:
"-49891384-ca44-4fc7-a7b9-fbbf353a2e7f"
isActive
:
true
lastModified
:
""
name
:
"Test Item"
version
:
"1"
__typename
:
"Child"
__proto__
:
Object
ChildContainer.sub.js:85 item in update:
{name: "Test Item", description: "", isActive: true, lastModified: "", created: 1520443086752, …}
created
:
1520443086752
description
:
""
id
:
"-49891384-ca44-4fc7-a7b9-fbbf353a2e7f"
isActive
:
true
lastModified
:
""
name
:
"Test Item"
version
:
"1"
__typename
:
"Child"
__proto__
:
Object
ChildContainer.sub.js:85 item in update:
{name: "Test Item", description: "", isActive: true, lastModified: "", created: 1520443086752, …}
created
:
1520443086752
description
:
""
id
:
"-49891384-ca44-4fc7-a7b9-fbbf353a2e7f"
isActive
:
true
lastModified
:
""
name
:
"Test Item"
version
:
"1"
__typename
:
"Child"
__proto__
:
Object
ChildContainer.sub.js:85 item in update:
{id: "ebc0f9f7-e537-4dd4-95a3-058ddad78d25", name: "Test Item", description: null, isActive: true, version: 1, …}
created
:
"1520443087400"
description
:
null
id
:
"ebc0f9f7-e537-4dd4-95a3-058ddad78d25"
isActive
:
true
lastModified
:
"1520443087400"
name
:
"Test Item"
version
:
1
__typename
:
"Child"
__proto__
:
Object
This might help:
I've gone ahead and set disableOffline: true in my AppSync instantiation and that fixes this.
While what's been said in the post on StackOverflow is definitely helpful, disableOffline: true is simply not a fix since having an offline capabilities is not an option for me, so I came up with the following..
Following code is a snippet of an App.js file from the AWS AppSync Docs
const NewPostWithData = graphql(NewPostMutation, {
props: (props) => ({ ... }),
options: {
refetchQueries: [{ query: AllPostsQuery }],
update: (dataProxy, { data: { addPost } }) => {
const query = AllPostsQuery;
const data = dataProxy.readQuery({ query });
console.log(addPost);
// S1
// data.allPost.posts.push(addPost); // Duplicates an item locally and throws following error: "Warning: Encountered two children with the same key, `14`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version."
// S2
const isIncluded = data.allPost.posts.some(post => addPost.id === post.id);
if (!isIncluded) {
data.allPost.posts = [
...data.allPost.posts,
addPost
];
}
// S3
// data.allPost.posts = [
// ...data.allPost.posts.filter((post) => post.id !== addPost.id),
// addPost
// ];
dataProxy.writeQuery({ query, data });
}
}
Since the options.update is invoked multiple times (3 times to my knowledge?) I'm just preventing to add data more than one time (data comes from optimisticResponse) to the local cache, otherwise error (S1) occurs and data will be added more than one time.
So in (S2) I'm basically looking for the added data with specific ID, if it exists just skip adding them again.
Or you can just replace the data (S3) with new array in which you spread all the data excluding the added and add it as last item.
Once response comes back, the "temporary" data are removed and response data are added to the cache.
This seems like a lot of coding gymnastics though. IMO AppSync should handle all of this for us—it's too low-level.
Thanks for the code sample @id-kemo. We are looking into making API improvements to the client experience for those that are looking to have an easier experience with offline programming using the Apollo client. Stay tuned.
any update on this guys?
Yeah, been trying AppSync today ... and immediately got hit by this bug!
I am using aws-appsync: 1.3.4 and aws-appsync-react:1.1.4 and am still facing an error:
Warning: Encountered two children with the same key
from an optimistic response on updating my cache after a mutation?
@manueliglesias I am using aws-appsync 1.4.0 and am still encountering this issue.
The new object is getting added to my local data both for the optimistic response and for the real response.
I am encountering this issue even when I remove my optimisticResponse property, however that is the easiest way to see what is happening.
And I can also confirm that adding disableOffline: true to my config removes this issue, and that the data is being added to local storage twice.
@nccurry @tqhoughton could you please open a new issue with code samples of how your interacting with the client and details including versions you're using? This is a closed issue and won't be tracked. There are internal bookkeeping semantics that the AppSync client uses for offline as well as how Apollo interacts with the network (hence why you see multiple responses and object tracking) but the best way for the team to provide assistance is with a new issue including all your environment details.
Sorry to pile on a closed ticket but I could not tell if the open tickets capture this same issue. I am still experiencing this.
"aws-appsync": "^1.7.1",
"aws-appsync-react": "^1.2.6",
I created a very simple app with the issue here: https://github.com/peterdyer7/todo-aws-amplify-appsync (to see the issue set disableOffline to false).
It is now late 2020 and this issue is happening for me as well. Offline capabilities don't work in AppSync at all anymore either. RIP AppSync SDK.
Most helpful comment
This seems like a lot of coding gymnastics though. IMO AppSync should handle all of this for us—it's too low-level.