Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Allow stale to be returned by exchanges #449

Merged
merged 3 commits into from
Oct 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/exchanges/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
publish,
Source,
Subject,
forEach,
scan,
toPromise,
} from 'wonka';
Expand Down Expand Up @@ -77,9 +78,13 @@ describe('on query', () => {

it('respects cache-and-network', () => {
const [ops$, next, complete] = input;
const result = jest.fn();
const exchange = cacheExchange(exchangeArgs)(ops$);

publish(exchange);
pipe(
exchange,
forEach(result)
);
next(queryOperation);

next({
Expand All @@ -93,6 +98,8 @@ describe('on query', () => {
complete();
expect(forwardedOperations.length).toBe(1);
expect(reexecuteOperation).toHaveBeenCalledTimes(1);
expect(result).toHaveBeenCalledTimes(2);
expect(result.mock.calls[1][0].stale).toBe(true);

expect(reexecuteOperation.mock.calls[0][0]).toEqual({
...queryOperation,
Expand Down
13 changes: 8 additions & 5 deletions src/exchanges/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,19 @@ export const cacheExchange: Exchange = ({ forward, client }) => {
filter(op => !shouldSkip(op) && isOperationCached(op)),
map(operation => {
const cachedResult = resultCache.get(operation.key);
if (operation.context.requestPolicy === 'cache-and-network') {
reexecuteOperation(client, operation);
}

return {
const result: OperationResult = {
...cachedResult,
operation: addMetadata(operation, {
cacheOutcome: cachedResult ? 'hit' : 'miss',
}),
};

if (operation.context.requestPolicy === 'cache-and-network') {
result.stale = true;
reexecuteOperation(client, operation);
}

return result;
})
);

Expand Down
1 change: 1 addition & 0 deletions src/hooks/__snapshots__/useMutation.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Object {
"error": undefined,
"extensions": undefined,
"fetching": false,
"stale": false,
}
`;
1 change: 1 addition & 0 deletions src/hooks/__snapshots__/useQuery.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Object {
"error": undefined,
"extensions": undefined,
"fetching": true,
"stale": false,
}
`;
1 change: 1 addition & 0 deletions src/hooks/__snapshots__/useSubscription.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Object {
"error": 5678,
"extensions": undefined,
"fetching": true,
"stale": false,
}
`;
7 changes: 5 additions & 2 deletions src/hooks/useMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CombinedError, createRequest } from '../utils';

export interface UseMutationState<T> {
fetching: boolean;
stale: boolean;
data?: T;
error?: CombinedError;
extensions?: Record<string, any>;
Expand All @@ -27,6 +28,7 @@ export const useMutation = <T = any, V = object>(

const [state, setState] = useState<UseMutationState<T>>({
fetching: false,
stale: false,
error: undefined,
data: undefined,
extensions: undefined,
Expand All @@ -36,6 +38,7 @@ export const useMutation = <T = any, V = object>(
(variables?: V, context?: Partial<OperationContext>) => {
setState({
fetching: true,
stale: false,
error: undefined,
data: undefined,
extensions: undefined,
Expand All @@ -47,8 +50,8 @@ export const useMutation = <T = any, V = object>(
client.executeMutation(request, context || {}),
toPromise
).then(result => {
const { data, error, extensions } = result;
setState({ fetching: false, data, error, extensions });
const { stale, data, error, extensions } = result;
setState({ fetching: false, stale: !!stale, data, error, extensions });
return result;
});
},
Expand Down
4 changes: 4 additions & 0 deletions src/hooks/useQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ describe('useQuery', () => {
const [state] = result.current;
expect(state).toEqual({
fetching: true,
stale: false,
extensions: undefined,
error: undefined,
data: undefined,
});
Expand Down Expand Up @@ -126,6 +128,8 @@ describe('useQuery', () => {
const [state] = result.current;
expect(state).toEqual({
fetching: false,
stale: false,
extensions: undefined,
error: 1,
data: 0,
});
Expand Down
10 changes: 8 additions & 2 deletions src/hooks/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useRequest } from './useRequest';

const initialState: UseQueryState<any> = {
fetching: false,
stale: false,
data: undefined,
error: undefined,
extensions: undefined,
Expand All @@ -26,6 +27,7 @@ export interface UseQueryArgs<V> {

export interface UseQueryState<T> {
fetching: boolean;
stale: boolean;
data?: T;
error?: CombinedError;
extensions?: Record<string, any>;
Expand Down Expand Up @@ -70,8 +72,9 @@ export const useQuery = <T = any, V = object>(
fromValue({ fetching: true }),
pipe(
query$,
map(({ data, error, extensions }) => ({
map(({ stale, data, error, extensions }) => ({
fetching: false,
stale: !!stale,
data,
error,
extensions,
Expand All @@ -82,7 +85,10 @@ export const useQuery = <T = any, V = object>(
]);
}),
// The individual partial results are merged into each previous result
scan((result, partial) => ({ ...result, ...partial }), initialState)
scan(
(result, partial) => ({ ...result, stale: false, ...partial }),
initialState
)
),
useMemo(() => (args.pause ? null : makeQuery$()), [args.pause, makeQuery$]),
initialState
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/useSubscription.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ describe('on subscription', () => {
* result of the state change.
*/
wrapper.update(<SubscriptionUser q={query} />);
expect(state).toEqual({ ...data, fetching: true });
expect(state).toEqual({
...data,
extensions: undefined,
fetching: true,
stale: false,
});
});
});

Expand Down
21 changes: 11 additions & 10 deletions src/hooks/useSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { OperationContext } from '../types';

const initialState: UseSubscriptionState<any> = {
fetching: false,
stale: false,
data: undefined,
error: undefined,
extensions: undefined,
Expand All @@ -26,6 +27,7 @@ export type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R;

export interface UseSubscriptionState<T> {
fetching: boolean;
stale: boolean;
data?: T;
error?: CombinedError;
extensions?: Record<string, any>;
Expand Down Expand Up @@ -71,8 +73,9 @@ export const useSubscription = <T = any, R = T, V = object>(
fromValue({ fetching: true }),
pipe(
subscription$,
map(({ data, error, extensions }) => ({
map(({ stale, data, error, extensions }) => ({
fetching: true,
stale: !!stale,
data,
error,
extensions,
Expand All @@ -86,15 +89,13 @@ export const useSubscription = <T = any, R = T, V = object>(
scan((result, partial: any) => {
const { current: handler } = handlerRef;
// If a handler has been passed, it's used to merge new data in
if (partial.data !== undefined && typeof handler === 'function') {
return {
...result,
...partial,
data: handler(result.data, partial.data),
};
} else {
return { ...result, ...partial };
}
const data =
partial.data !== undefined
? typeof handler === 'function'
? handler(result.data, partial.data)
: partial.data
: result.data;
return { ...result, stale: false, ...partial, data };
}, initialState)
),
useMemo(() => (args.pause ? null : makeSubscription$()), [
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export interface OperationResult<Data = any> {
error?: CombinedError;
/** Optional extensions return by the Graphql server. */
extensions?: Record<string, any>;
/** Optional stale flag added by exchanges that return stale results. */
stale?: boolean;
}

/** Input parameters for to an Exchange factory function. */
Expand Down