diff --git a/.changeset/hungry-experts-admire.md b/.changeset/hungry-experts-admire.md new file mode 100644 index 0000000000..e486762c21 --- /dev/null +++ b/.changeset/hungry-experts-admire.md @@ -0,0 +1,5 @@ +--- +'@urql/exchange-graphcache': minor +--- + +Allow scalar values on the parent to be accessed from `parent[info.fieldName]` consistently. Prior to this change `parent[fieldAlias]` would get populated, which wouldn’t always result in a field that’s consistently accessible. diff --git a/.changeset/tame-ways-obey.md b/.changeset/tame-ways-obey.md new file mode 100644 index 0000000000..e4c358c46c --- /dev/null +++ b/.changeset/tame-ways-obey.md @@ -0,0 +1,5 @@ +--- +'@urql/exchange-graphcache': patch +--- + +Fix cases where `ResolveInfo`’s `parentFieldKey` was incorrectly populated with a key that isn’t a field key (allowing for `cache.resolve(info.parentKey, info.parentFieldKey)` to be possible) but was instead set to `info.parentKey` combined with the field key. diff --git a/exchanges/graphcache/src/helpers/dict.ts b/exchanges/graphcache/src/helpers/dict.ts deleted file mode 100644 index ac3cd501f8..0000000000 --- a/exchanges/graphcache/src/helpers/dict.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const makeDict = (): any => Object.create(null); - -export const isDictEmpty = (x: any) => { - for (const _ in x) return false; - return true; -}; diff --git a/exchanges/graphcache/src/operations/query.ts b/exchanges/graphcache/src/operations/query.ts index e87f1045e4..10aeff4f48 100644 --- a/exchanges/graphcache/src/operations/query.ts +++ b/exchanges/graphcache/src/operations/query.ts @@ -421,18 +421,25 @@ const readSelection = ( // The field is a scalar and can be retrieved directly from the result dataFieldValue = resultValue; } else if (InMemoryData.currentOperation === 'read' && resolver) { - // We have to update the information in context to reflect the info - // that the resolver will receive - updateContext(ctx, output, typename, entityKey, key, fieldName); - // We have a resolver for this field. - // Prepare the actual fieldValue, so that the resolver can use it - if (fieldValue !== undefined) { - output[fieldAlias] = fieldValue; + // Prepare the actual fieldValue, so that the resolver can use it, + // as to avoid the user having to do `cache.resolve(parent, info.fieldKey)` + // only to get a scalar value. + let parent = output; + if (node.selectionSet === undefined && fieldValue !== undefined) { + parent = { + ...output, + [fieldAlias]: fieldValue, + [fieldName]: fieldValue, + }; } + // We have to update the information in context to reflect the info + // that the resolver will receive + updateContext(ctx, parent, typename, entityKey, fieldKey, fieldName); + dataFieldValue = resolver( - output, + parent, fieldArgs || ({} as Variables), store, ctx diff --git a/exchanges/graphcache/src/operations/write.ts b/exchanges/graphcache/src/operations/write.ts index f5bbfe2a47..cf8f74c5bc 100644 --- a/exchanges/graphcache/src/operations/write.ts +++ b/exchanges/graphcache/src/operations/write.ts @@ -354,7 +354,7 @@ const writeSelection = ( data, typename, entityKey || typename, - joinKeys(typename, fieldKey), + fieldKey, fieldName ); diff --git a/exchanges/graphcache/src/store/data.ts b/exchanges/graphcache/src/store/data.ts index 7346eb7457..87f78c2606 100644 --- a/exchanges/graphcache/src/store/data.ts +++ b/exchanges/graphcache/src/store/data.ts @@ -19,7 +19,6 @@ import { joinKeys, } from './keys'; -import { makeDict } from '../helpers/dict'; import { invariant, currentDebugStack } from '../helpers/help'; type Dict = Record; @@ -283,7 +282,7 @@ const setNode = ( // On the map itself we get or create the entity as a dict let entity = keymap.get(entityKey) as Dict; if (entity === undefined) { - keymap.set(entityKey, (entity = makeDict())); + keymap.set(entityKey, (entity = Object.create(null))); } // If we're setting undefined we delete the node's entry @@ -614,7 +613,7 @@ export const persistData = () => { if (currentData!.storage) { currentOptimistic = true; currentOperation = 'read'; - const entries: SerializedEntries = makeDict(); + const entries: SerializedEntries = {}; for (const key of currentData!.persist.keys()) { const { entityKey, fieldKey } = deserializeKeyInfo(key); let x: void | Link | EntityField; diff --git a/exchanges/graphcache/src/store/store.test.ts b/exchanges/graphcache/src/store/store.test.ts index 041575059d..40222efe68 100644 --- a/exchanges/graphcache/src/store/store.test.ts +++ b/exchanges/graphcache/src/store/store.test.ts @@ -425,7 +425,7 @@ describe('Store with OptimisticMutationConfig', () => { randomData, 'Todo', 'Todo:1', - 'Todo:1.createdAt', + 'createdAt', 'createdAt' );