-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(fe2): greatly improved DX for apollo cache modification #2831
Conversation
({ variables, details: { DELETE } }) => { | ||
if (variables.id === workspaceId) return DELETE | ||
({ variables, helpers: { evict } }) => { | ||
if (variables.id === workspaceId) return evict() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return evict() replaces return DELETE
@@ -0,0 +1,59 @@ | |||
const { reduce } = require('lodash') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to introduce a custom GQL codegen plugin to get this to work
* createUpdatedValue() helper to build a new value with updated fields. | ||
*/ | ||
value: ReadonlyDeep<ModifyObjectFieldValue<Type, Field>> | ||
helpers: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Access all of the helpers you need from the helpers
object in the modifyObjectField() callback
* | ||
* This function operates on a deeply cloned value that is safe to mutate | ||
*/ | ||
createUpdatedValue: ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplifies this:
modifyObjectField(
cache,
workspaceCacheId,
'projects',
({ value, helpers: { ref } }) => {
const newItems = isUndefined(value?.items)
? undefined
: [ref('Project', newProject.id), ...value.items]
const newTotalCount = isUndefined(value?.totalCount)
? undefined
: (value.totalCount || 0) + 1
return {
...value,
...(isUndefined(newItems) ? {} : { items: newItems }),
...(isUndefined(newTotalCount) ? {} : { totalCount: newTotalCount })
}
}
)
Into this:
modifyObjectField(
cache,
workspaceCacheId,
'projects',
({ helpers: { ref, createUpdatedValue } }) => {
return createUpdatedValue(({ update }) => {
update('items', (items) => [
ref('Project', newProject.id),
...items
])
update('totalCount', (count) => count + 1)
})
}
)
update() calls are properly typed as well, and will only allow you to specify property paths that actually exist in value
, and the property value in the callback is gonna be properly typed too
]) | ||
update('totalCount', (count) => count + 1) | ||
}), | ||
{ autoEvictFiltered: true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Option that automatically evicts all fields that have variables w/ filters in them. You usually want to do this when adding a new item to a list - it may be hard to tell if the item fits the filtered query, and it'll just be easier to evict that version of the query and refetch it
cc @cdriesler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 😄 Thanks for the visuals and clear explanations as well!
(Built this when I had some downtime about a week ago, hope you like it :) )
There's 2 big parts to this:
modifyObjectField()
function. You no longer need to specify any generic arguments, it will figure out what part of the cache you're updating based on the cache reference and field name you pass into the function.modifyObjectFIeld()
, most importantly introducedcreateUpdatedValue
that updates the cached value at specific paths (e.g.items.[0].name
) ONLY if such a value exists in the cache. This makes it easier to ensure we only update cached values, not introduce new ones (and get an invalid state that way - e.g. a totalCount of 0, when it was undefined before)Demo of all of the TS auto typing you get out of the box in type name, field name, field value, variable value resolution
![Code_pwLJH7zfr7](https://private-user-images.githubusercontent.com/938316/363172777-710bbf99-0e0f-4d7f-921c-51c4e7d4afe4.gif?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTcyNzc3LTcxMGJiZjk5LTBlMGYtNGQ3Zi05MjFjLTUxYzRlN2Q0YWZlNC5naWY_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1lNmUwZTgxOTZlZTE1ZDI0NWVhNzY0YTZiOTFmY2ZlZjQ5ZTYzMTRjMjhmMjM4OGIxZWE3Y2Q1OWQ1MDIzMGJlJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.BczwfLhPSAqWTA_lDHGU04Irjfr2RnsoXf7dI-PSU7I)
getCacheId() blocks invalid type names:
![image](https://private-user-images.githubusercontent.com/938316/363173048-5691726b-7992-4ab7-8516-6c048286728a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTczMDQ4LTU2OTE3MjZiLTc5OTItNGFiNy04NTE2LTZjMDQ4Mjg2NzI4YS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kZGQ1ODBhMmQzYjMzMDRmNDkwMjE3ZDNjMDY3NGU3YzY0NThmYzRhODJmZThkN2ZhMTVjZDBlNDNkMWQ1YzQ5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.LSWbABFrR0M02Rp-hvVqZuFMdAYmOPmPqo0SNjoCz5o)
readField() allows reading fields of
![image](https://private-user-images.githubusercontent.com/938316/363173597-35de8b0c-2667-4d70-80dd-db8b1c347c8f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTczNTk3LTM1ZGU4YjBjLTI2NjctNGQ3MC04MGRkLWRiOGIxYzM0N2M4Zi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT04YWRlMzMzZGE1NzQxOGU0MjRiMmZmZGU4NmU0MzNjOWJkZGEyMjI2MWE3OWFkMThjOWYwYzdkM2YyOGJkMjk2JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.S7jVNp5n4mQTmqMmtpKWjS_OHJHEV5wr8qMc7lK18Rk)
![image](https://private-user-images.githubusercontent.com/938316/363173683-b905668c-0c93-4235-8b77-b45b77533cd4.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTczNjgzLWI5MDU2NjhjLTBjOTMtNDIzNS04Yjc3LWI0NWI3NzUzM2NkNC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT03MmZiODMyZGE0NzhhMWVhYjM0NDJlZDQwNzY2NzZlZjM2MDY5ODk2OGQ3NDU4NWYxZDU3YjE1ZGZiMmEzMjg5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.BWgR4khhqxIYIwfV_wf-76Bw41STsCFjOtdvB-cusTg)
Reference
objects, and its now fully typed - meaning it will understand what kind of reference are you specifying and only support fields that exist on that typeReference objects are typed - you can no longer mismatch references, e.g. put a User reference where a Project reference is expected:
![image](https://private-user-images.githubusercontent.com/938316/363174058-48fcd40f-3f43-4116-bf6d-d24d805f271a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTc0MDU4LTQ4ZmNkNDBmLTNmNDMtNDExNi1iZjZkLWQyNGQ4MDVmMjcxYS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT00MzRmY2QyNTBjYTFlMmQ0M2JlMDY2ZjA3ODQyZThmYjE4YTczMTEyZjQ0Y2ZjOWQ1Mjc1NGNiMzY4NGUxYjIzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.PCfNTNb-zqFx8xAeyNwClToS_N54FaobRFMwBGFgacw)
![image](https://private-user-images.githubusercontent.com/938316/363174232-63e19b4b-821e-4c21-bfe5-8fa7a3cffe91.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk3NTc3MDgsIm5iZiI6MTczOTc1NzQwOCwicGF0aCI6Ii85MzgzMTYvMzYzMTc0MjMyLTYzZTE5YjRiLTgyMWUtNGMyMS1iZmU1LThmYTdhM2NmZmU5MS5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjE3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIxN1QwMTU2NDhaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yZThmMWQzMTQzNmUxYTE2ODQ0OGU0YjA0MzMwOTZmMTU4OTBkNTU0ZmJhM2E0NDFhMzhlMDQzZmRlYmQ2YTlkJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.39dzShUSPHzVY4BVdTFi8gESOVFr7Ufl8688WBPH0ts)