diff --git a/src/cache/inmemory/policies.ts b/src/cache/inmemory/policies.ts index c361a5a8c6f..b546b3e8328 100644 --- a/src/cache/inmemory/policies.ts +++ b/src/cache/inmemory/policies.ts @@ -865,69 +865,57 @@ function makeFieldFunctionOptions( storage, cache: policies.cache, canRead, - readField: makeReadFieldFunction( - policies, - objectOrReference, - context, - ), + readField() { + return policies.readField( + normalizeReadFieldOptions(arguments, objectOrReference, context), + context, + ); + }, mergeObjects: makeMergeObjectsFunction(context.store), }; } -export function makeReadFieldFunction( - policies: Policies, +export function normalizeReadFieldOptions( + readFieldArgs: IArguments, objectOrReference: StoreObject | Reference | undefined, - context: ReadMergeModifyContext, -): ReadFieldFunction & { - normalizeOptions(args: IArguments): ReadFieldOptions; -} { - function normalizeOptions(readFieldArgs: IArguments): ReadFieldOptions { - const { - 0: fieldNameOrOptions, - 1: from, - length: argc, - } = readFieldArgs; - - let options: ReadFieldOptions; - - if (typeof fieldNameOrOptions === "string") { - options = { - fieldName: fieldNameOrOptions, - // Default to objectOrReference only when no second argument was - // passed for the from parameter, not when undefined is explicitly - // passed as the second argument. - from: argc > 1 ? from : objectOrReference, - }; - } else { - options = { ...fieldNameOrOptions }; - // Default to objectOrReference only when fieldNameOrOptions.from is - // actually omitted, rather than just undefined. - if (!hasOwn.call(options, "from")) { - options.from = objectOrReference; - } - } - - if (__DEV__ && options.from === void 0) { - invariant.warn(`Undefined 'from' passed to readField with arguments ${ - stringifyForDisplay(Array.from(readFieldArgs)) - }`); + variables: ReadMergeModifyContext["variables"], +): ReadFieldOptions { + const { + 0: fieldNameOrOptions, + 1: from, + length: argc, + } = readFieldArgs; + + let options: ReadFieldOptions; + + if (typeof fieldNameOrOptions === "string") { + options = { + fieldName: fieldNameOrOptions, + // Default to objectOrReference only when no second argument was + // passed for the from parameter, not when undefined is explicitly + // passed as the second argument. + from: argc > 1 ? from : objectOrReference, + }; + } else { + options = { ...fieldNameOrOptions }; + // Default to objectOrReference only when fieldNameOrOptions.from is + // actually omitted, rather than just undefined. + if (!hasOwn.call(options, "from")) { + options.from = objectOrReference; } + } - if (void 0 === options.variables) { - options.variables = context.variables; - } + if (__DEV__ && options.from === void 0) { + invariant.warn(`Undefined 'from' passed to readField with arguments ${ + stringifyForDisplay(Array.from(readFieldArgs)) + }`); + } - return options; + if (void 0 === options.variables) { + options.variables = variables; } - return Object.assign(function readField() { - return policies.readField( - normalizeOptions(arguments), - context, - ); - }, { - normalizeOptions, - }); + return options; } function makeMergeObjectsFunction( diff --git a/src/cache/inmemory/writeToStore.ts b/src/cache/inmemory/writeToStore.ts index 5d9c4d93325..2db1ee88402 100644 --- a/src/cache/inmemory/writeToStore.ts +++ b/src/cache/inmemory/writeToStore.ts @@ -35,8 +35,8 @@ import { InMemoryCache } from './inMemoryCache'; import { EntityStore } from './entityStore'; import { Cache } from '../../core'; import { canonicalStringify } from './object-canon'; -import { makeReadFieldFunction } from './policies'; -import { ReadFieldFunction, ReadFieldOptions } from '../core/types/common'; +import { normalizeReadFieldOptions } from './policies'; +import { ReadFieldFunction } from '../core/types/common'; export interface WriteContext extends ReadMergeModifyContext { readonly written: { @@ -242,24 +242,36 @@ export class StoreWriter { incoming.__typename = typename; } - let lazyReadField: undefined | ReturnType; + // This readField function will be passed as context.readField in the + // KeyFieldsContext object created within policies.identify (called below). + // In addition to reading from the existing context.store (thanks to the + // policies.readField(options, context) line at the very bottom), this + // version of readField can read from Reference objects that are currently + // pending in context.incomingById, which is important whenever keyFields + // need to be extracted from a child object that processSelectionSet has + // turned into a Reference. const readField: ReadFieldFunction = function (this: void) { - const read = lazyReadField || ( - lazyReadField = makeReadFieldFunction(policies, incoming, context)); - - const options: ReadFieldOptions = read.normalizeOptions(arguments); + const options = normalizeReadFieldOptions( + arguments, + incoming, + context.variables, + ); if (isReference(options.from)) { const info = context.incomingById.get(options.from.__ref); if (info) { - const result = read({ ...options, from: info.storeObject }); + const result = policies.readField({ + ...options, + from: info.storeObject + }, context); + if (result !== void 0) { return result; } } } - return read(options); + return policies.readField(options, context); }; const fieldNodeSet = new Set();