Skip to content

Commit

Permalink
fix(*): fix calling subscription fn multiple times when `subscription…
Browse files Browse the repository at this point in the history
…Key` is not a string
  • Loading branch information
kaciakmaciak committed Oct 16, 2022
1 parent 7c1ef77 commit a7f32f3
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 29 deletions.
12 changes: 6 additions & 6 deletions src/__tests__/subscription-storage.spec.ts
Expand Up @@ -38,14 +38,14 @@ describe('subscription storage', () => {
const observerFn = jest.fn();
const { subscription, next } = subscriptionFactory(observerFn);

storeSubscription(queryClient, ['test'], subscription);
storeSubscription(queryClient, 'test', subscription);

next('value-1');
expect(observerFn).toHaveBeenCalledTimes(1);
expect(observerFn).toHaveBeenCalledWith('value-1');
observerFn.mockClear();

cleanupSubscription(queryClient, ['test']);
cleanupSubscription(queryClient, 'test');

next('value-2');
expect(observerFn).not.toHaveBeenCalled();
Expand All @@ -59,8 +59,8 @@ describe('subscription storage', () => {
const { subscription: subscriptionB, next: nextB } =
subscriptionFactory(observerBFn);

storeSubscription(queryClient, ['testA'], subscriptionA);
storeSubscription(queryClient, ['testB'], subscriptionB);
storeSubscription(queryClient, 'testA', subscriptionA);
storeSubscription(queryClient, 'testB', subscriptionB);

nextA('A1');
expect(observerAFn).toHaveBeenCalledTimes(1);
Expand All @@ -71,7 +71,7 @@ describe('subscription storage', () => {
expect(observerBFn).toHaveBeenCalledWith('B1');
observerBFn.mockClear();

cleanupSubscription(queryClient, ['testA']);
cleanupSubscription(queryClient, 'testA');

nextA('A2');
expect(observerAFn).not.toHaveBeenCalled();
Expand All @@ -82,7 +82,7 @@ describe('subscription storage', () => {

it('should not fail when key does not exist', () => {
expect(() =>
cleanupSubscription(queryClient, ['test-non-existing'])
cleanupSubscription(queryClient, 'test-non-existing')
).not.toThrow();
});
});
14 changes: 5 additions & 9 deletions src/subscription-storage.ts
@@ -1,4 +1,4 @@
import { QueryKey, hashQueryKey, QueryClient } from 'react-query';
import type { QueryClient } from 'react-query';
import { Subscription } from 'rxjs';

const clientCacheSubscriptionsKey = ['__activeSubscriptions__'];
Expand All @@ -10,16 +10,15 @@ type SubscriptionStorage = Map<string, SubscriptionStorageItem>;
/**
* Stores subscription by its key and `pageParam` in the clientCache.
*/
export function storeSubscription<TSubscriptionKey extends QueryKey = QueryKey>(
export function storeSubscription(
queryClient: QueryClient,
subscriptionKey: TSubscriptionKey,
hashedSubscriptionKey: string,
subscription: Subscription,
pageParam?: string
) {
const activeSubscriptions: SubscriptionStorage =
queryClient.getQueryData(clientCacheSubscriptionsKey) || new Map();

const hashedSubscriptionKey = hashQueryKey(subscriptionKey);
const previousSubscription = activeSubscriptions.get(hashedSubscriptionKey);

let newSubscriptionValue: SubscriptionStorageItem;
Expand All @@ -38,17 +37,14 @@ export function storeSubscription<TSubscriptionKey extends QueryKey = QueryKey>(
/**
* Removes stored subscription by its key and `pageParam` from the clientCache.
*/
export function cleanupSubscription<
TSubscriptionKey extends QueryKey = QueryKey
>(
export function cleanupSubscription(
queryClient: QueryClient,
subscriptionKey: TSubscriptionKey,
hashedSubscriptionKey: string,
pageParam?: string
) {
const activeSubscriptions: SubscriptionStorage =
queryClient.getQueryData(clientCacheSubscriptionsKey) || new Map();

const hashedSubscriptionKey = hashQueryKey(subscriptionKey);
const subscription = activeSubscriptions.get(hashedSubscriptionKey);

if (!subscription) return;
Expand Down
10 changes: 7 additions & 3 deletions src/use-infinite-subscription.ts
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useInfiniteQuery, useQueryClient } from 'react-query';
import { useInfiniteQuery, useQueryClient, hashQueryKey } from 'react-query';
import type {
QueryKey,
UseInfiniteQueryResult,
Expand Down Expand Up @@ -161,6 +161,8 @@ export function useInfiniteSubscription<
TSubscriptionKey
> = {}
): UseInfiniteSubscriptionResult<TData, TError> {
const hashedSubscriptionKey = hashQueryKey(subscriptionKey);

const { queryFn, clearErrors } = useObservableQueryFn(
subscriptionFn,
(data, previousData, pageParam): InfiniteData<TSubscriptionFnData> => {
Expand Down Expand Up @@ -225,10 +227,12 @@ export function useInfiniteSubscription<
?.getObserversCount();

if (activeObserversCount === 0) {
cleanupSubscription(queryClient, subscriptionKey);
cleanupSubscription(queryClient, hashedSubscriptionKey);
}
};
}, [queryClient, subscriptionKey]);
// This is safe as `hashedSubscriptionKey` is derived from `subscriptionKey`.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryClient, hashedSubscriptionKey]);

return queryResult;
}
21 changes: 13 additions & 8 deletions src/use-observable-query-fn.ts
@@ -1,5 +1,5 @@
import { useRef } from 'react';
import { useQueryClient } from 'react-query';
import { useQueryClient, hashQueryKey } from 'react-query';
import type {
QueryFunction,
QueryKey,
Expand Down Expand Up @@ -43,7 +43,8 @@ export function useObservableQueryFn<
const queryFn: QueryFunction<TSubscriptionFnData, TSubscriptionKey> = (
context
) => {
const { queryKey, pageParam, signal } = context;
const { queryKey: subscriptionKey, pageParam, signal } = context;
const hashedSubscriptionKey = hashQueryKey(subscriptionKey);

if (failRefetchWith.current) {
throw failRefetchWith.current;
Expand All @@ -58,7 +59,7 @@ export function useObservableQueryFn<
// If we do not invalidate the query, the hook will never re-subscribe,
// as data are otherwise marked as fresh.
function cancel() {
queryClient.invalidateQueries(queryKey, undefined, {
queryClient.invalidateQueries(subscriptionKey, undefined, {
cancelRefetch: false,
});
}
Expand All @@ -75,27 +76,31 @@ export function useObservableQueryFn<
}

// @todo: Skip subscription for SSR
cleanupSubscription(queryClient, queryKey, pageParam ?? undefined);
cleanupSubscription(
queryClient,
hashedSubscriptionKey,
pageParam ?? undefined
);

const subscription = stream$
.pipe(
skip(1),
tap((data) => {
queryClient.setQueryData(queryKey, (previousData) =>
queryClient.setQueryData(subscriptionKey, (previousData) =>
dataUpdater(data, previousData, pageParam)
);
}),
catchError((error) => {
failRefetchWith.current = error;
queryClient.setQueryData(queryKey, (data) => data, {
queryClient.setQueryData(subscriptionKey, (data) => data, {
// To make the retryOnMount work
// @see: https://github.com/tannerlinsley/react-query/blob/9e414e8b4f3118b571cf83121881804c0b58a814/src/core/queryObserver.ts#L727
updatedAt: 0,
});
return of(undefined);
}),
finalize(() => {
queryClient.invalidateQueries(queryKey, undefined, {
queryClient.invalidateQueries(subscriptionKey, undefined, {
cancelRefetch: false,
});
})
Expand All @@ -106,7 +111,7 @@ export function useObservableQueryFn<
// see `cleanup` fn for more info
storeSubscription(
queryClient,
queryKey,
hashedSubscriptionKey,
subscription,
pageParam ?? undefined
);
Expand Down
10 changes: 7 additions & 3 deletions src/use-subscription.ts
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { useQuery, useQueryClient, hashQueryKey } from 'react-query';
import type {
QueryKey,
UseQueryResult,
Expand Down Expand Up @@ -115,6 +115,8 @@ export function useSubscription<
TSubscriptionKey
> = {}
): UseSubscriptionResult<TData, TError> {
const hashedSubscriptionKey = hashQueryKey(subscriptionKey);

const { queryFn, clearErrors } = useObservableQueryFn(
subscriptionFn,
(data) => data
Expand Down Expand Up @@ -161,10 +163,12 @@ export function useSubscription<
?.getObserversCount();

if (activeObserversCount === 0) {
cleanupSubscription(queryClient, subscriptionKey);
cleanupSubscription(queryClient, hashedSubscriptionKey);
}
};
}, [queryClient, subscriptionKey]);
// This is safe as `hashedSubscriptionKey` is derived from `subscriptionKey`.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryClient, hashedSubscriptionKey]);

return queryResult;
}

0 comments on commit a7f32f3

Please sign in to comment.