成功重试时 Apollo-link @client 变异

Apollo-link @client mutation on successful retry

我在我的 React 应用程序中呈现因网络错误导致的重试通知。我希望清除任何此类通知 if/when 重新建立连接(重试成功)

我已经使用 apollo-link-retry 并使用自定义 attempts 回调在重试循环开始和超时时改变缓存。这行得通,但是当重试成功时,通知会保留在屏幕上,因为重试成功时不会调用回调,所以我无法从缓存中清除通知。

我试过使用 apollo-link-error 实现类似的逻辑并遇到类似的问题。 link 仅在发生错误且成功重试不是错误时调用。

这是我的 apollo-link-retry 配置,"almost" 有效:

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

也许我需要实施自定义 link 才能完成此任务?我希望找到一种方法来利用 apollo-link-retry 的良好重试逻辑,并在逻辑进行时另外发出一些状态来缓存。

我通过做两件事实现了预期的行为:

通过 attempts 函数在 link 上下文中维护重试计数:

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

实施自定义 link 订阅错误和已完成的事件进一步向下 link 链并使用新的上下文字段来决定是否应该发出通知:

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