From 1a412feac120d16454b8c81295c48e153ee9c3ad Mon Sep 17 00:00:00 2001 From: Sajjad Hashemian Date: Wed, 24 Apr 2019 11:52:57 +0430 Subject: [PATCH 1/2] fix refetch after error --- src/useQuery.ts | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/useQuery.ts b/src/useQuery.ts index 17a7c47..4260031 100644 --- a/src/useQuery.ts +++ b/src/useQuery.ts @@ -188,6 +188,8 @@ export function useQuery< return; } + let subscription: ZenObservable.Subscription | undefined; + const invalidateCurrentResult = () => { // A hack to get rid React warnings during tests. The default // implementation of `actHack` just invokes the callback immediately. @@ -197,16 +199,45 @@ export function useQuery< setResponseId(x => x + 1); }); }; - const subscription = observableQuery.subscribe( - invalidateCurrentResult, - invalidateCurrentResult - ); + + // from: https://github.com/apollographql/react-apollo/blob/master/src/Query.tsx#L363 + // after a error on refetch, without this fix, refetch never works again + function invalidateErrorResult() { + unsubscribe(); + + const lastError = observableQuery.getLastError(); + const lastResult = observableQuery.getLastResult(); + + if (!suspend) { + observableQuery.resetLastResults(); + subscribe(); + } + + Object.assign(observableQuery, { lastError, lastResult }); + + actHack(() => { + setResponseId(x => x + 1); + }); + } invalidateCachedObservableQuery(client, watchQueryOptions); - return () => { - subscription.unsubscribe(); - }; + function subscribe() { + subscription = observableQuery.subscribe( + invalidateCurrentResult, + invalidateErrorResult + ); + } + + function unsubscribe() { + if (subscription) { + subscription.unsubscribe(); + } + subscription = undefined; + } + + subscribe(); + return unsubscribe; }, [shouldSkip, observableQuery] ); From 5cc29fca3dc7d142dd27b01600dccd2d7f4b9e7e Mon Sep 17 00:00:00 2001 From: Sajjad Hashemian Date: Mon, 13 May 2019 10:39:27 +0430 Subject: [PATCH 2/2] add test --- src/__tests__/useQuery-test.tsx | 92 ++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/__tests__/useQuery-test.tsx b/src/__tests__/useQuery-test.tsx index 5163cb3..5f182db 100644 --- a/src/__tests__/useQuery-test.tsx +++ b/src/__tests__/useQuery-test.tsx @@ -131,16 +131,21 @@ function Tasks({ query, ...options }: TasksProps) { interface TasksWrapperProps extends TasksProps { client?: ApolloClient; + TasksComponent?: React.FC; } const SuspenseCompat = ({ children }: SuspenseProps) => <>{children}; -function TasksWrapper({ client, ...props }: TasksWrapperProps) { +function TasksWrapper({ + client, + TasksComponent = Tasks, + ...props +}: TasksWrapperProps) { const SuspenseComponent = props.suspend !== false ? Suspense : SuspenseCompat; const inner = ( Loading with suspense}> - + ); @@ -452,6 +457,89 @@ it('should support updating query variables with suspense', async () => { `); }); +it('should refetch the query', async () => { + const MY_TASKS_MOCKS: MockedResponse[] = [ + { + request: { query: TASKS_QUERY, variables: {} }, + result: { + data: { __typename: 'Query' }, + errors: [new GraphQLError('Simulating GraphQL error')], + }, + }, + { + request: { query: TASKS_QUERY, variables: {} }, + result: { + data: { __typename: 'Query', tasks: [...SAMPLE_TASKS] }, + }, + }, + ]; + + const client = createClient({ mocks: MY_TASKS_MOCKS }); + + function MyTasks({ query, ...options }: TasksProps) { + const { data, error, loading, refetch } = useQuery(query, { + ...options, + }); + const refetched = React.useRef(false); + + React.useEffect( + () => { + if (!loading && !refetched.current) { + refetched.current = true; + refetch(); + } + }, + [loading] + ); + + if (error) { + return <>{error.message}; + } + + if (loading) { + return <>Loading without suspense; + } + + if (!data) { + return <>Skipped loading of data; + } + + return ; + } + + const { container } = render( + + ); + + expect(container).toMatchInlineSnapshot(` +
+ Loading without suspense +
+`); + + await wait(); + + expect(container).toMatchInlineSnapshot(` +
+
    +
  • + Learn GraphQL +
  • +
  • + Learn React +
  • +
  • + Learn Apollo +
  • +
+
+`); +}); + it("shouldn't suspend if the data is already cached", async () => { const client = createMockClient(); const { container, rerender } = render(