Apollo GraphQL updateQuery to typePolicy

Sep 01 2020

Je me cogne la tête contre un mur. Je l' ai mis à jour Apollo 3, et ne peut pas comprendre comment migrer un updateQueryà un typePolicy. Je fais une pagination de base basée sur la continuation, et c'est ainsi que j'avais l'habitude de fusionner les résultats de fetchMore:

await fetchMore({
  query: MessagesByThreadIDQuery,
  variables: {
    threadId: threadId,
    limit: Configuration.MessagePageSize,
    continuation: token
  },
  updateQuery: (prev, curr) => {
    // Extract our updated message page.
    const last = prev.messagesByThreadId.messages ?? []
    const next = curr.fetchMoreResult?.messagesByThreadId.messages ?? []

    return {
      messagesByThreadId: {
        __typename: 'MessagesContinuation',
        messages: [...last, ...next],
        continuation: curr.fetchMoreResult?.messagesByThreadId.continuation
      }
    }
  }

J'ai tenté d'écrire le merge typePolicymoi - même, mais il se charge et génère continuellement des erreurs sur les identifiants en double dans le cache Apollo. Voici à quoi typePolicyressemble ma requête.

  typePolicies: {
    Query: {
      fields: {
        messagesByThreadId: {
          keyArgs: false,
          merge: (existing, incoming, args): IMessagesContinuation => {
            const typedExisting: IMessagesContinuation | undefined = existing
            const typedIncoming: IMessagesContinuation | undefined = incoming
            const existingMessages = (typedExisting?.messages ?? [])
            const incomingMessages = (typedIncoming?.messages ?? [])

            const result = existing ? {
              __typename: 'MessageContinuation',
              messages: [...existingMessages, ...incomingMessages],
              continuation: typedIncoming?.continuation
            } : incoming


            return result
          }
        }
      }
    }
  }

Réponses

DylanVester Sep 15 2020 at 14:21

J'ai donc pu résoudre mon cas d'utilisation. Cela semble bien plus difficile qu'il ne devrait l'être. Je dois essentiellement essayer de localiser les éléments existants correspondant aux éléments entrants et de les écraser, ainsi que d'ajouter de nouveaux éléments qui n'existent pas encore dans le cache.

Je dois également appliquer cette logique uniquement si un jeton de continuation a été fourni, car s'il est nul ou non défini, je devrais simplement utiliser la valeur entrante car cela indique que nous effectuons un chargement initial.

Mon document a la forme suivante:

{
  "items": [{ id: string, ...others }],
  "continuation": "some_token_value"
}

J'ai créé une politique de type générique que je peux utiliser pour tous mes documents qui ont une forme similaire. Cela me permet de spécifier le nom de la propriété items, les arguments clés sur lesquels je veux mettre en cache et le nom du type graphql.

export function ContinuationPolicy(keyArgs: Array<string>, itemPropertyKey: string, typeName: string) {
  return {
    keyArgs,
    merge(existing: any, incoming: any, args: any) {
      if (!!existing && !!args.args?.continuation) {
        const existingItems = (existing ? existing[itemPropertyKey] : [])
        const incomingItems = (incoming ? incoming[itemPropertyKey] : [])
        let items: Array<any> = [...existingItems]

        for (let i = 0; i < incomingItems.length; i++) {
          const current = incomingItems[i] as any
          const found = items.findIndex(m => m.__ref === current.__ref)

          if (found > -1) {
            items[found] === current
          } else {
            items = [...items, current]
          }
        }

        // This new data is a continuation of the last data.
        return {
          __typename: typeName,
          [itemPropertyKey]: items,
          continuation: incoming.continuation
        }
      } else {
        // When we have no existing data in the cache, we'll just use the incoming data.
        return incoming
      }
    }
  }
}