问题描述
我正在遵循有关GraphQl的教程,在视频中,作者不使用Refetcheries进行DELETEMONT,并且所有这些都可以很好地与UI更新和突变一起使用.但是在项目中,Sandbox代码已更新,现在在作业组件上使用Refetchquery-> line 20-> deletejob(): codesandbox .
我在我的应用程序中存在类似的问题,该问题不会自动更新UI,而无需在任何地方进行杂乱无章.如果我理解正确的话,Apollo不应通过Apollo-Cache-Inmemory自动应用Apollo的缓存,以这种突变进行突变并更新UI.
用阿波罗助推器开箱即用的示例:
export default gql` mutation deleteItem($id: uuid!) { delete_item(where: {id:{_eq: $id }}){ returning { id } } }`;
const onDeleteItem = (id) => { deleteItem({ variables: { id }, }); };
对此有任何建议或经验?
推荐答案
答案相对简单:GraphQl中没有通用方法告诉客户端已删除实体.让我们首先将其与更新突变进行比较.想象一下,我们正在更新缓存中已经拥有的工作之一.首先,缓存(简化,实际上并不完全是Apollo内部的外观):
{ "Query": { "jobs": ["Job:1", "Job:2"], }, "Job:1": { "__typename": "Job", "id": 1, "company": "Big Corp", "title": "Sales Specialist" }, "Job:2": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Expert" } }
如果阿波罗现在从更新突变中获得答案,该突变如下:
{ "data": { "updateJob": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Unicorn" } } }
它可以使用dataIdFromObject函数来了解对象属于我们的归一化缓存中的缓存键"Job:2". Apollo可以假设此版本比旧版本更新,并与较新的结果偏爱合并钥匙.我们的缓存现在看起来像这样:
{ "Query": { "jobs": ["Job:1", "Job:2"], }, "Job:1": { ... }, "Job:2": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Unicorn" // updated! } }
然后,"jobs"查询将自动使用新作业更新,因为它只是引用了作业,而不是存储实体本身.伟大的!但是现在比较删除函数的结果:
{ "data": { "deleteJob": { "returning": { "id": 2, } } } }
此查询的结果可能是什么.阿波罗不知道您刚刚删除了具有某个ID的工作.也许如果GraphQl在规范中有一些神奇的" __ ISDEATED",我们会得到类似的东西:
{ "data": { "deleteJob": { "__typename": "Job", "__isDeleted": true, "id": 2, } } } }
我们可以给我们的缓存实施一个提示,即应从所有引用查询中删除具有__isDeleted: true的实体.但不幸的是,这不存在.但是,这还不错,我们可以使用refetchQuery触发其他查询的拔出,或者我们可以手动更新其他查询:
const deleteJob = useMutation(DELETE_JOB, { update(store, response) { const data = store.readQuery({ query: GET_JOBS }); data.jobs = data.jobs.filter(job => job.id !== response.deleteJob.returning.id); store.writeQuery({ query: GET_JOBS, data }); } });
问题描述
I am following a tutorial on GraphQL, in the video the author does not use refetchQueries for a deleteMutation and all works well with UI updates and mutation. But here in the project sandbox code is updated and refetchQuery is now used for this operatio on Job component -> line 20 -> deleteJob(): codeSandBox.
I have this similar problem in my app that does not update the UI automatically without refetchQueries done everywhere. Shouldn't Apollo be applying automatically the cache of Apollo via apollo-cache-inmemory, perform mutation and update UI in this kind of mutation if I understand it right.
Example out of the box with apollo-boost:
export default gql` mutation deleteItem($id: uuid!) { delete_item(where: {id:{_eq: $id }}){ returning { id } } }`;
const onDeleteItem = (id) => { deleteItem({ variables: { id }, }); };
Any suggestions or experiences on this?
推荐答案
The answer is relatively simple: There is no universal way in GraphQL to tell a client that an entity was deleted. Let's first compare this to an update mutations. Imagine we are updating one of the jobs that we already have in our cache. First the cache (simplified, not actually quite how it looks inside of Apollo):
{ "Query": { "jobs": ["Job:1", "Job:2"], }, "Job:1": { "__typename": "Job", "id": 1, "company": "Big Corp", "title": "Sales Specialist" }, "Job:2": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Expert" } }
If Apollo now gets an answer from an update mutation that looks like the following:
{ "data": { "updateJob": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Unicorn" } } }
It can use the dataIdFromObject function to understand that the object belongs to the cache key "Job:2" in our normalised cache. Apollo can assume that this version is newer than the old one and merge the keys with preference of the newer result. Our cache now looks like this:
{ "Query": { "jobs": ["Job:1", "Job:2"], }, "Job:1": { ... }, "Job:2": { "__typename": "Job", "id": 2, "company": "Big Corp", "title": "GraphQL Unicorn" // updated! } }
Then the "jobs" query will automatically update with the new job because it is just referencing the job and is not storing the entity itself. Great! But now compare the result from the delete function:
{ "data": { "deleteJob": { "returning": { "id": 2, } } } }
The result of this query could be anything. Apollo cannot know that you have just deleted a job with a certain id. Maybe if GraphQL had something in the specification like a magical "__isDeleted" and we would get something like:
{ "data": { "deleteJob": { "__typename": "Job", "__isDeleted": true, "id": 2, } } } }
We could give our cache implementation the hint that entities with __isDeleted: true should be removed from all referencing queries. But unfortunately this does not exists. This is not to bad though, we can either use refetchQuery to trigger a refetch of the other query or we can manually update the other query:
const deleteJob = useMutation(DELETE_JOB, { update(store, response) { const data = store.readQuery({ query: GET_JOBS }); data.jobs = data.jobs.filter(job => job.id !== response.deleteJob.returning.id); store.writeQuery({ query: GET_JOBS, data }); } });