Skip to content

Commit

Permalink
Handle case where loadQuery was called with a different environment
Browse files Browse the repository at this point in the history
Reviewed By: josephsavona

Differential Revision: D21955516

fbshipit-source-id: 487e87a5b86300c6bfbedca4c57abee9e63a7b63
  • Loading branch information
rbalicki2 authored and facebook-github-bot committed Jun 11, 2020
1 parent 2ae19c3 commit 08bb2d2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 13 deletions.
1 change: 1 addition & 0 deletions packages/relay-experimental/EntryPointTypes.flow.js
Expand Up @@ -86,6 +86,7 @@ export type PreloadedQueryInner<
+fetchPolicy: PreloadFetchPolicy,
+id: ?string,
+name: string,
+networkCacheConfig: ?CacheConfig,
+source: ?Observable<GraphQLResponse>,
+kind: 'PreloadedQuery',
+variables: $ElementType<TQuery, 'variables'>,
Expand Down
47 changes: 47 additions & 0 deletions packages/relay-experimental/__tests__/usePreloadedQuery-test.js
Expand Up @@ -929,5 +929,52 @@ describe('usePreloadedQuery', () => {
});
});
});

describe('when environments do not match', () => {
it('should fetch the data at render time, even if the query has already resolved', () => {
let altDataSource;
const altFetch = jest.fn((_query, _variables, _cacheConfig) =>
Observable.create(sink => {
altDataSource = sink;
}),
);
const altEnvironment = new Environment({
network: Network.create(altFetch),
store: new Store(new RecordSource()),
});
const prefetched = loadQuery(environment, preloadableConcreteRequest, {
id: '4',
});
let data;
expect(dataSource).toBeDefined();
if (dataSource) {
dataSource.next(response);
}
TestRenderer.act(() => jest.runAllImmediates());

expect(altFetch).not.toHaveBeenCalled();
function Component(props) {
data = usePreloadedQuery(query, props.prefetched);
return data.node.name;
}
const renderer = TestRenderer.create(
<RelayEnvironmentProvider environment={altEnvironment}>
<React.Suspense fallback="Fallback">
<Component prefetched={prefetched} />
</React.Suspense>
</RelayEnvironmentProvider>,
);

expect(renderer.toJSON()).toEqual('Fallback');
expect(altFetch).toHaveBeenCalledTimes(1);
expect(altDataSource).toBeDefined();
if (altDataSource) {
altDataSource.next(response);
}

TestRenderer.act(() => jest.runAllImmediates());
expect(renderer.toJSON()).toEqual('Zuck');
});
});
});
});
14 changes: 10 additions & 4 deletions packages/relay-experimental/loadQuery.js
Expand Up @@ -69,6 +69,10 @@ function loadQuery<TQuery: OperationType, TEnvironmentProviderOptions>(
);

const fetchPolicy = options?.fetchPolicy ?? 'store-or-network';
const networkCacheConfig = {
...options?.networkCacheConfig,
force: true,
};

let madeNetworkRequest = false;
const makeNetworkRequest = (params): Observable<GraphQLResponse> => {
Expand All @@ -77,10 +81,11 @@ function loadQuery<TQuery: OperationType, TEnvironmentProviderOptions>(

madeNetworkRequest = true;
const network = environment.getNetwork();
const sourceObservable = network.execute(params, variables, {
force: true,
...options?.networkCacheConfig,
});
const sourceObservable = network.execute(
params,
variables,
networkCacheConfig,
);

const subject = new ReplaySubject();
sourceObservable.subscribe({
Expand Down Expand Up @@ -213,6 +218,7 @@ function loadQuery<TQuery: OperationType, TEnvironmentProviderOptions>(
},
id: moduleId,
name: params.name,
networkCacheConfig,
fetchPolicy,
source: madeNetworkRequest ? returnedObservable : undefined,
variables,
Expand Down
32 changes: 23 additions & 9 deletions packages/relay-experimental/usePreloadedQuery.js
Expand Up @@ -17,11 +17,11 @@ const invariant = require('invariant');
const useLazyLoadQueryNode = require('./useLazyLoadQueryNode');
const useMemoOperationDescriptor = require('./useMemoOperationDescriptor');
const useRelayEnvironment = require('./useRelayEnvironment');
const warning = require('warning');

const {useTrackLoadQueryInRender} = require('./loadQuery');
const {useDebugValue} = require('react');
const {
Observable,
__internal: {fetchQueryDeduped},
} = require('relay-runtime');

Expand Down Expand Up @@ -88,14 +88,28 @@ function usePreloadedQuery<TQuery: OperationType>(
// Thus, if two calls to loadQuery are made with the same environment and identifier
// (i.e. the same request is made twice), the second query will be deduped
// and components will suspend for the duration of the first query.
const dedupedSource =
source != null
? fetchQueryDeduped(
environment,
operation.request.identifier,
() => source,
)
: null;
const dedupedSource = fetchQueryDeduped(
environment,
operation.request.identifier,
() => {
if (source && environment === preloadedQuery.environment) {
return source;
} else {
// if a call to loadQuery is made with a particular environment, and that
// preloaded query is passed to usePreloadedQuery in a different environmental
// context, we cannot re-use the existing preloaded query. Instead, we must
// re-execute the query with the new environment (at render time.)
// TODO T68036756 track occurences of this warning and turn it into a hard error
warning(
false,
'usePreloadedQuery(): usePreloadedQuery was passed a preloaded query ' +
'that was created with a different environment than the one that is currently ' +
'in context. In the future, this will become a hard error.',
);
return environment.execute({operation});
}
},
);
useLazyLoadQueryNodeParams = {
componentDisplayName: 'usePreloadedQuery()',
Expand Down

0 comments on commit 08bb2d2

Please sign in to comment.