Skip to content

Commit 9f7fb21

Browse files
captbaritonefacebook-github-bot
authored andcommitted
Reenable warning if fetchQuery is called in render (using new unsatable React APIs)
Reviewed By: tyao1 Differential Revision: D70524672 fbshipit-source-id: 242db550f62dafdd58596e0e93e46e3e0114ff55
1 parent 651cc27 commit 9f7fb21

10 files changed

+100
-2
lines changed

packages/react-relay/relay-hooks/__tests__/loadQuery-test.js

+51-1
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import type {
2626
Variables,
2727
} from 'relay-runtime';
2828

29-
const {loadQuery} = require('../loadQuery');
29+
const {loadQuery, useTrackLoadQueryInRender} = require('../loadQuery');
3030
// Need React require for OSS build
3131
// eslint-disable-next-line no-unused-vars
3232
const React = require('react');
33+
const ReactTestRenderer = require('react-test-renderer');
3334
const {
3435
Network,
3536
Observable,
@@ -40,6 +41,7 @@ const {
4041
createMockEnvironment,
4142
disallowConsoleErrors,
4243
disallowWarnings,
44+
expectWarningWillFire,
4345
} = require('relay-test-utils-internal');
4446

4547
disallowWarnings();
@@ -945,4 +947,52 @@ describe('loadQuery', () => {
945947
});
946948
});
947949
});
950+
951+
describe('warnings', () => {
952+
let Container;
953+
let LoadDuringRender;
954+
955+
beforeEach(() => {
956+
Container = (props: {children: React.Node}) => {
957+
// $FlowFixMe[react-rule-hook]
958+
useTrackLoadQueryInRender();
959+
return props.children;
960+
};
961+
LoadDuringRender = (props: {name?: ?string}) => {
962+
loadQuery(environment, preloadableConcreteRequest, variables, {
963+
fetchPolicy: 'store-or-network',
964+
__nameForWarning: props.name,
965+
});
966+
return null;
967+
};
968+
});
969+
970+
it('warns if called during render', () => {
971+
expectWarningWillFire(
972+
'Relay: `loadQuery` should not be called inside a React render function.',
973+
);
974+
975+
ReactTestRenderer.act(() => {
976+
ReactTestRenderer.create(
977+
<Container>
978+
<LoadDuringRender />
979+
</Container>,
980+
);
981+
});
982+
});
983+
984+
it('uses provided name for warning', () => {
985+
expectWarningWillFire(
986+
'Relay: `refetch` should not be called inside a React render function.',
987+
);
988+
989+
ReactTestRenderer.act(() => {
990+
ReactTestRenderer.create(
991+
<Container>
992+
<LoadDuringRender name="refetch" />
993+
</Container>,
994+
);
995+
});
996+
});
997+
});
948998
});

packages/react-relay/relay-hooks/__tests__/useQueryLoader-live-query-test.js

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const loadQuery = jest.fn().mockImplementation(() => {
5656

5757
jest.mock('../loadQuery', () => ({
5858
loadQuery,
59+
useTrackLoadQueryInRender: () => {},
5960
}));
6061

6162
beforeEach(() => {

packages/react-relay/relay-hooks/__tests__/useQueryLoader-test.js

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const loadQuery = jest.fn().mockImplementation(() => {
5757

5858
jest.mock('../loadQuery', () => ({
5959
loadQuery,
60+
useTrackLoadQueryInRender: () => {},
6061
}));
6162

6263
describe.each([

packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-test.js

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {
3838
import type {OperationDescriptor, Variables} from 'relay-runtime';
3939
import type {Query} from 'relay-runtime/util/RelayRuntimeTypes';
4040

41+
const {useTrackLoadQueryInRender} = require('../loadQuery');
4142
const RelayEnvironmentProvider = require('../RelayEnvironmentProvider');
4243
const useRefetchableFragmentInternal = require('../useRefetchableFragmentInternal');
4344
const invariant = require('invariant');
@@ -368,6 +369,7 @@ describe.each([['New', useRefetchableFragmentInternal]])(
368369
};
369370

370371
const ContextProvider = ({children}: {children: React.Node}) => {
372+
useTrackLoadQueryInRender();
371373
const [env, _setEnv] = useState(environment);
372374
const relayContext = useMemo(() => ({environment: env}), [env]);
373375

packages/react-relay/relay-hooks/loadQuery.js

+25
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import type {
3131
import type {OperationAvailability} from 'relay-runtime/store/RelayStoreTypes';
3232

3333
const invariant = require('invariant');
34+
const React = require('react');
3435
const {
3536
__internal: {fetchQueryDeduped},
3637
Observable,
@@ -40,9 +41,21 @@ const {
4041
getRequest,
4142
getRequestIdentifier,
4243
} = require('relay-runtime');
44+
const warning = require('warning');
4345

46+
let RenderDispatcher = null;
4447
let fetchKey = 100001;
4548

49+
hook useTrackLoadQueryInRender() {
50+
if (RenderDispatcher === null) {
51+
// Flow does not know of React internals (rightly so), but we need to
52+
// ensure here that this function isn't called inside render.
53+
RenderDispatcher =
54+
// $FlowFixMe[prop-missing]
55+
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE?.H;
56+
}
57+
}
58+
4659
type QueryType<T> =
4760
T extends Query<infer V, infer D, infer RR>
4861
? {
@@ -75,6 +88,17 @@ function loadQuery<
7588
options?: ?LoadQueryOptions,
7689
environmentProviderOptions?: ?TEnvironmentProviderOptions,
7790
): PreloadedQueryInner<TQuery, TEnvironmentProviderOptions> {
91+
// This code ensures that we don't call loadQuery during render.
92+
const CurrentDispatcher =
93+
// $FlowFixMe[prop-missing]
94+
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE?.H;
95+
96+
warning(
97+
RenderDispatcher == null || CurrentDispatcher !== RenderDispatcher,
98+
'Relay: `%s` should not be called inside a React render function.',
99+
options?.__nameForWarning ?? 'loadQuery',
100+
);
101+
78102
// Every time you call loadQuery, we will generate a new fetchKey.
79103
// This will ensure that every query reference that is created and
80104
// passed to usePreloadedQuery is independently evaluated,
@@ -392,4 +416,5 @@ function loadQuery<
392416

393417
module.exports = {
394418
loadQuery,
419+
useTrackLoadQueryInRender,
395420
};

packages/react-relay/relay-hooks/useEntryPointLoader.js

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type {
2121
} from './EntryPointTypes.flow';
2222

2323
const loadEntryPoint = require('./loadEntryPoint');
24+
const {useTrackLoadQueryInRender} = require('./loadQuery');
2425
const useIsMountedRef = require('./useIsMountedRef');
2526
const {useCallback, useEffect, useRef, useState} = require('react');
2627

@@ -103,6 +104,8 @@ hook useLoadEntryPoint<
103104
* entry point references.
104105
*/
105106

107+
useTrackLoadQueryInRender();
108+
106109
const initialEntryPointReferenceInternal =
107110
options?.TEST_ONLY__initialEntryPointData?.entryPointReference ??
108111
initialNullEntryPointReferenceState;

packages/react-relay/relay-hooks/useFragment.js

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import type {Fragment, FragmentType, GraphQLTaggedNode} from 'relay-runtime';
1515

16+
const {useTrackLoadQueryInRender} = require('./loadQuery');
1617
const useFragmentInternal = require('./useFragmentInternal');
1718
const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
1819
const {useDebugValue} = require('react');
@@ -48,6 +49,10 @@ declare hook useFragment<TFragmentType: FragmentType, TData>(
4849
): ?TData;
4950

5051
hook useFragment(fragment: GraphQLTaggedNode, key: mixed): mixed {
52+
// We need to use this hook in order to be able to track if
53+
// loadQuery was called during render
54+
useTrackLoadQueryInRender();
55+
5156
const fragmentNode = getFragment(fragment);
5257
useStaticFragmentNodeWarning(fragmentNode, 'first argument of useFragment()');
5358
const data = useFragmentInternal(fragmentNode, key, 'useFragment()');

packages/react-relay/relay-hooks/useLazyLoadQuery.js

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {
1919
Variables,
2020
} from 'relay-runtime';
2121

22+
const {useTrackLoadQueryInRender} = require('./loadQuery');
2223
const useLazyLoadQueryNode = require('./useLazyLoadQueryNode');
2324
const useMemoOperationDescriptor = require('./useMemoOperationDescriptor');
2425
const useRelayEnvironment = require('./useRelayEnvironment');
@@ -50,6 +51,10 @@ hook useLazyLoadQuery<TVariables: Variables, TData>(
5051
UNSTABLE_renderPolicy?: RenderPolicy,
5152
},
5253
): TData {
54+
// We need to use this hook in order to be able to track if
55+
// loadQuery was called during render
56+
useTrackLoadQueryInRender();
57+
5358
const environment = useRelayEnvironment();
5459

5560
const query = useMemoOperationDescriptor(

packages/react-relay/relay-hooks/usePreloadedQuery.js

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
} from './EntryPointTypes.flow';
1919
import type {Query, RenderPolicy, Variables} from 'relay-runtime';
2020

21+
const {useTrackLoadQueryInRender} = require('./loadQuery');
2122
const useLazyLoadQueryNode = require('./useLazyLoadQueryNode');
2223
const useMemoOperationDescriptor = require('./useMemoOperationDescriptor');
2324
const useRelayEnvironment = require('./useRelayEnvironment');
@@ -66,6 +67,10 @@ hook usePreloadedQuery<
6667
UNSTABLE_renderPolicy?: RenderPolicy,
6768
},
6869
): TData {
70+
// We need to use this hook in order to be able to track if
71+
// loadQuery was called during render
72+
useTrackLoadQueryInRender();
73+
6974
const environment = useRelayEnvironment();
7075
const {fetchKey, fetchPolicy, source, variables, networkCacheConfig} =
7176
preloadedQuery;

packages/react-relay/relay-hooks/useQueryLoader.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
Variables,
2424
} from 'relay-runtime';
2525

26-
const {loadQuery} = require('./loadQuery');
26+
const {loadQuery, useTrackLoadQueryInRender} = require('./loadQuery');
2727
const useIsMountedRef = require('./useIsMountedRef');
2828
const useQueryLoader_EXPERIMENTAL = require('./useQueryLoader_EXPERIMENTAL');
2929
const useRelayEnvironment = require('./useRelayEnvironment');
@@ -170,6 +170,7 @@ hook useQueryLoader_CURRENT<
170170
initialQueryReference ?? initialNullQueryReferenceState;
171171

172172
const environment = useRelayEnvironment();
173+
useTrackLoadQueryInRender();
173174

174175
const isMountedRef = useIsMountedRef();
175176
const undisposedQueryReferencesRef = useRef<

0 commit comments

Comments
 (0)