Apollo GraphQL updateQuery para typePolicy

Sep 01 2020

Estou batendo minha cabeça contra a parede. Atualizei para a Apollo 3 e não consigo descobrir como migrar um updateQuerypara um typePolicy. Estou fazendo paginação básica com base em continuação e é assim que costumava mesclar os resultados 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
      }
    }
  }

Eu tentei escrever o merge typePolicymesmo, mas ele carrega continuamente e lança erros sobre identificadores duplicados no cache do Apollo. Aqui está o que typePolicyparece para minha consulta.

  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
          }
        }
      }
    }
  }

Respostas

DylanVester Sep 15 2020 at 14:21

Então, consegui resolver meu caso de uso. Parece muito mais difícil do que realmente precisa ser. Basicamente, tenho que tentar localizar os itens existentes que correspondem aos que chegam e sobrescrevê-los, bem como adicionar quaisquer novos itens que ainda não existam no cache.

Também tenho que aplicar essa lógica apenas se um token de continuação for fornecido, porque se for nulo ou indefinido, devo apenas usar o valor de entrada, porque isso indica que estamos fazendo um carregamento inicial.

Meu documento tem o formato deste:

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

Eu criei uma política de tipo genérico que posso usar para todos os meus documentos que têm um formato semelhante. Ele me permite especificar o nome da propriedade de itens, quais são os argumentos-chave que desejo armazenar em cache e o nome do tipo 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
      }
    }
  }
}