Apollo-link @client мутация при успешной повторной попытке

Я отображаю уведомления в своем приложении React о повторных попытках, вызванных сетевыми ошибками. Я хочу очистить любое такое уведомление, если / когда соединение восстановлено (успешная повторная попытка)

Я использовал apollo-link-retry и использовал обычай attempts обратный вызов для изменения кеша, когда начинается повторный цикл и когда он истекает. Это работает, но уведомления остаются на экране, когда происходит успешная повторная попытка, потому что обратный вызов не вызывается при успешной повторной попытке, поэтому у меня нет способа удалить уведомления из кэша.

Я пытался реализовать аналогичную логику, используя apollo-link-error с похожей проблемой. Ссылка вызывается только тогда, когда произошла ошибка, и успешная повторная попытка не является ошибкой.

Вот моя конфигурация apollo-link-retry который "почти" работает:

const retryLink = new RetryLink({
  attempts: (count) => {
    let notifyType
    let shouldRetry = true
    if (count === 1) {
      notifyType = 'CONNECTION_RETRY'
      shouldRetry = true
    } else if (count <= 30) {
      shouldRetry = true
    } else {
      notifyType = 'CONNECTION_TIMEOUT'
      shouldRetry = false
    }
    if (notifyType) {
      client.mutate({
        mutation: gql`
          mutation m($notification: Notification!) {
            raiseNotification(notification: $notification) @client
          }
        `,
        variables: {
          notification: { type: notifyType }
        }
      })
    }
    return shouldRetry
  }
})

Может быть, мне нужно реализовать пользовательскую ссылку для достижения этой цели? Я надеюсь найти способ использовать приятную логику повторов apollo-link-retry и дополнительно испускает некоторое состояние для кэширования по мере продвижения логики.

1 ответ

Решение

Я достиг желаемого поведения, сделав две вещи:

Поддерживать количество повторов в контексте ссылки через attempts функция:

new RetryLink({
  delay: {
    initial: INITIAL_RETRY_DELAY,
    max: MAX_RETRY_DELAY,
    jitter: true
  },
  attempts: (count, operation, error) => {
    if (!error.message || error.message !== 'Failed to fetch') {
      // If error is not related to connection, do not retry
      return false
    }
    operation.setContext(context => ({ ...context, retryCount: count }))
    return (count <= MAX_RETRY_COUNT)
  }
})

Реализуйте пользовательскую ссылку, которая подписывается на ошибки и завершенные события далее по цепочке ссылок и использует новое поле контекста, чтобы решить, следует ли создавать уведомления:

new ApolloLink((operation, forward) => {
  const context = operation.getContext()
  return new Observable(observer => {
    let subscription, hasApplicationError
    try {
      subscription = forward(operation).subscribe({
        next: result => {
          if (result.errors) {
            // Encountered application error (not network related)
            hasApplicationError = true
            notifications.raiseNotification(apolloClient, 'UNEXPECTED_ERROR')
          }
          observer.next(result)
        },
        error: networkError => {
          // Encountered network error
          if (context.retryCount === 1) {
            // Started retrying
            notifications.raiseNotification(apolloClient, 'CONNECTION_RETRY')
          }
          if (context.retryCount === MAX_RETRY_COUNT) {
            // Timed out after retrying
            notifications.raiseNotification(apolloClient, 'CONNECTION_TIMEOUT')
          }
          observer.error(networkError)
        },
        complete: () => {
          if (!hasApplicationError) {
            // Completed successfully after retrying
            notifications.clearNotification(apolloClient)
          }
          observer.complete.bind(observer)()
        },
      })
    } catch (e) {
      observer.error(e)
    }
    return () => {
      if (subscription) subscription.unsubscribe()
    }
  })
})
Другие вопросы по тегам