react-relay 17.0.0 → 18.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/ReactRelayContainerUtils.js.flow +2 -2
  2. package/ReactRelayContext.js +1 -1
  3. package/ReactRelayContext.js.flow +1 -1
  4. package/ReactRelayFragmentContainer.js.flow +2 -2
  5. package/ReactRelayPaginationContainer.js.flow +2 -2
  6. package/ReactRelayQueryRenderer.js.flow +1 -1
  7. package/ReactRelayQueryRendererContext.js.flow +1 -1
  8. package/ReactRelayRefetchContainer.js.flow +2 -2
  9. package/ReactRelayTypes.js.flow +45 -18
  10. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +2 -2
  11. package/buildReactRelayContainer.js.flow +6 -5
  12. package/hooks.js +1 -1
  13. package/index.js +1 -1
  14. package/legacy.js +1 -1
  15. package/lib/relay-hooks/getConnectionState.js +47 -0
  16. package/lib/relay-hooks/legacy/FragmentResource.js +2 -6
  17. package/lib/relay-hooks/loadEntryPoint.js +8 -5
  18. package/lib/relay-hooks/loadQuery.js +2 -14
  19. package/lib/relay-hooks/readFragmentInternal.js +2 -4
  20. package/lib/relay-hooks/useEntryPointLoader.js +5 -8
  21. package/lib/relay-hooks/useFragment.js +4 -7
  22. package/lib/relay-hooks/useFragmentInternal.js +6 -484
  23. package/lib/relay-hooks/useFragmentInternal_CURRENT.js +477 -0
  24. package/lib/relay-hooks/useFragmentInternal_EXPERIMENTAL.js +499 -0
  25. package/lib/relay-hooks/useLazyLoadQuery.js +2 -5
  26. package/lib/relay-hooks/useLoadMoreFunction.js +10 -43
  27. package/lib/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js +130 -0
  28. package/lib/relay-hooks/usePreloadedQuery.js +6 -9
  29. package/lib/relay-hooks/useQueryLoader.js +9 -3
  30. package/lib/relay-hooks/useQueryLoader_EXPERIMENTAL.js +120 -0
  31. package/multi-actor/ActorChange.js.flow +1 -1
  32. package/package.json +3 -3
  33. package/react-relay-hooks.js +2 -2
  34. package/react-relay-hooks.min.js +2 -2
  35. package/react-relay-legacy.js +1 -1
  36. package/react-relay-legacy.min.js +1 -1
  37. package/react-relay.js +2 -2
  38. package/react-relay.min.js +2 -2
  39. package/relay-hooks/EntryPointTypes.flow.js.flow +35 -12
  40. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +8 -4
  41. package/relay-hooks/MatchContainer.js.flow +1 -1
  42. package/relay-hooks/ProfilerContext.js.flow +1 -1
  43. package/relay-hooks/__flowtests__/EntryPointTypes/ExtractQueryTypes-flowtest.js.flow +43 -0
  44. package/relay-hooks/getConnectionState.js.flow +97 -0
  45. package/relay-hooks/legacy/FragmentResource.js.flow +2 -13
  46. package/relay-hooks/loadEntryPoint.js.flow +10 -4
  47. package/relay-hooks/loadQuery.js.flow +4 -28
  48. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +4 -1
  49. package/relay-hooks/readFragmentInternal.js.flow +1 -10
  50. package/relay-hooks/useEntryPointLoader.js.flow +3 -4
  51. package/relay-hooks/useFragment.js.flow +0 -5
  52. package/relay-hooks/useFragmentInternal.js.flow +19 -643
  53. package/relay-hooks/useFragmentInternal_CURRENT.js.flow +656 -0
  54. package/relay-hooks/useFragmentInternal_EXPERIMENTAL.js.flow +718 -0
  55. package/relay-hooks/useLazyLoadQuery.js.flow +0 -5
  56. package/relay-hooks/useLoadMoreFunction.js.flow +14 -80
  57. package/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js.flow +280 -0
  58. package/relay-hooks/usePaginationFragment.js.flow +1 -1
  59. package/relay-hooks/usePreloadedQuery.js.flow +0 -5
  60. package/relay-hooks/useQueryLoader.js.flow +28 -5
  61. package/relay-hooks/useQueryLoader_EXPERIMENTAL.js.flow +253 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {Direction, ReaderFragment} from 'relay-runtime';
15
+
16
+ const invariant = require('invariant');
17
+ const {ConnectionInterface, getValueAtPath} = require('relay-runtime');
18
+
19
+ function getConnectionState(
20
+ direction: Direction,
21
+ fragmentNode: ReaderFragment,
22
+ fragmentData: mixed,
23
+ connectionPathInFragmentData: $ReadOnlyArray<string | number>,
24
+ ): {
25
+ cursor: ?string,
26
+ hasMore: boolean,
27
+ } {
28
+ const {
29
+ EDGES,
30
+ PAGE_INFO,
31
+ HAS_NEXT_PAGE,
32
+ HAS_PREV_PAGE,
33
+ END_CURSOR,
34
+ START_CURSOR,
35
+ } = ConnectionInterface.get();
36
+ const connection = getValueAtPath(fragmentData, connectionPathInFragmentData);
37
+ if (connection == null) {
38
+ return {cursor: null, hasMore: false};
39
+ }
40
+
41
+ invariant(
42
+ typeof connection === 'object',
43
+ 'Relay: Expected connection in fragment `%s` to have been `null`, or ' +
44
+ 'a plain object with %s and %s properties. Instead got `%s`.',
45
+ fragmentNode.name,
46
+ EDGES,
47
+ PAGE_INFO,
48
+ connection,
49
+ );
50
+
51
+ const edges = connection[EDGES];
52
+ const pageInfo = connection[PAGE_INFO];
53
+ if (edges == null || pageInfo == null) {
54
+ return {cursor: null, hasMore: false};
55
+ }
56
+
57
+ invariant(
58
+ Array.isArray(edges),
59
+ 'Relay: Expected connection in fragment `%s` to have a plural `%s` field. ' +
60
+ 'Instead got `%s`.',
61
+ fragmentNode.name,
62
+ EDGES,
63
+ edges,
64
+ );
65
+ invariant(
66
+ typeof pageInfo === 'object',
67
+ 'Relay: Expected connection in fragment `%s` to have a `%s` field. ' +
68
+ 'Instead got `%s`.',
69
+ fragmentNode.name,
70
+ PAGE_INFO,
71
+ pageInfo,
72
+ );
73
+
74
+ const cursor =
75
+ direction === 'forward'
76
+ ? pageInfo[END_CURSOR] ?? null
77
+ : pageInfo[START_CURSOR] ?? null;
78
+ invariant(
79
+ cursor === null || typeof cursor === 'string',
80
+ 'Relay: Expected page info for connection in fragment `%s` to have a ' +
81
+ 'valid `%s`. Instead got `%s`.',
82
+ fragmentNode.name,
83
+ START_CURSOR,
84
+ cursor,
85
+ );
86
+
87
+ let hasMore;
88
+ if (direction === 'forward') {
89
+ hasMore = cursor != null && pageInfo[HAS_NEXT_PAGE] === true;
90
+ } else {
91
+ hasMore = cursor != null && pageInfo[HAS_PREV_PAGE] === true;
92
+ }
93
+
94
+ return {cursor, hasMore};
95
+ }
96
+
97
+ module.exports = getConnectionState;
@@ -452,7 +452,7 @@ class FragmentResourceImpl {
452
452
  missingLiveResolverFields(snapshot)?.map(({liveStateID}) => {
453
453
  const store = environment.getStore();
454
454
 
455
- // $FlowFixMe[prop-missing] This is expected to be a LiveResolverStore
455
+ // $FlowFixMe[prop-missing] This is expected to be a RelayModernStore
456
456
  return store.getLiveResolverPromise(liveStateID);
457
457
  }) ?? [];
458
458
 
@@ -559,21 +559,12 @@ class FragmentResourceImpl {
559
559
  _throwOrLogErrorsInSnapshot(snapshot: SingularOrPluralSnapshot) {
560
560
  if (Array.isArray(snapshot)) {
561
561
  snapshot.forEach(s => {
562
- handlePotentialSnapshotErrors(
563
- this._environment,
564
- s.missingRequiredFields,
565
- s.relayResolverErrors,
566
- s.errorResponseFields,
567
- s.selector.node.metadata?.throwOnFieldError ?? false,
568
- );
562
+ handlePotentialSnapshotErrors(this._environment, s.errorResponseFields);
569
563
  });
570
564
  } else {
571
565
  handlePotentialSnapshotErrors(
572
566
  this._environment,
573
- snapshot.missingRequiredFields,
574
- snapshot.relayResolverErrors,
575
567
  snapshot.errorResponseFields,
576
- snapshot.selector.node.metadata?.throwOnFieldError ?? false,
577
568
  );
578
569
  }
579
570
  }
@@ -773,8 +764,6 @@ class FragmentResourceImpl {
773
764
  missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
774
765
  seenRecords: currentSnapshot.seenRecords,
775
766
  selector: currentSnapshot.selector,
776
- missingRequiredFields: currentSnapshot.missingRequiredFields,
777
- relayResolverErrors: currentSnapshot.relayResolverErrors,
778
767
  errorResponseFields: currentSnapshot.errorResponseFields,
779
768
  };
780
769
  if (updatedData !== renderData) {
@@ -17,13 +17,15 @@ import type {
17
17
  EnvironmentProviderOptions,
18
18
  IEnvironmentProvider,
19
19
  PreloadedEntryPoint,
20
+ PreloadedQuery,
20
21
  } from './EntryPointTypes.flow';
21
22
 
22
23
  const {loadQuery} = require('./loadQuery');
23
24
 
24
25
  function loadEntryPoint<
25
26
  TEntryPointParams: {...},
26
- TPreloadedQueries: {...},
27
+ // $FlowExpectedError[unclear-type] Need any to make it supertype of all PreloadedQuery
28
+ TPreloadedQueries: {+[string]: PreloadedQuery<any>},
27
29
  TPreloadedEntryPoints: {...},
28
30
  TRuntimeProps: {...},
29
31
  TExtraProps,
@@ -53,8 +55,12 @@ function loadEntryPoint<
53
55
  if (queries != null) {
54
56
  const queriesPropNames = Object.keys(queries);
55
57
  queriesPropNames.forEach(queryPropName => {
58
+ const query = queries[queryPropName];
59
+ if (query == null) {
60
+ return;
61
+ }
56
62
  const {environmentProviderOptions, options, parameters, variables} =
57
- queries[queryPropName];
63
+ query;
58
64
 
59
65
  const environment = environmentProvider.getEnvironment(
60
66
  environmentProviderOptions,
@@ -86,11 +92,11 @@ function loadEntryPoint<
86
92
  entryPointDescription;
87
93
  preloadedEntryPoints[entryPointPropName] = loadEntryPoint<
88
94
  _,
89
- {...},
95
+ {},
90
96
  {...},
91
97
  {...},
92
98
  mixed,
93
- EntryPointComponent<{...}, {...}, {...}, mixed>,
99
+ EntryPointComponent<{}, {...}, {...}, mixed>,
94
100
  _,
95
101
  >(environmentProvider, nestedEntryPoint, nestedParams);
96
102
  });
@@ -30,7 +30,6 @@ import type {
30
30
  } from 'relay-runtime';
31
31
 
32
32
  const invariant = require('invariant');
33
- const React = require('react');
34
33
  const {
35
34
  __internal: {fetchQueryDeduped},
36
35
  Observable,
@@ -41,30 +40,19 @@ const {
41
40
  getRequest,
42
41
  getRequestIdentifier,
43
42
  } = require('relay-runtime');
44
- const warning = require('warning');
45
43
 
46
- let RenderDispatcher = null;
47
44
  let fetchKey = 100001;
48
45
 
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.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
56
- ?.ReactCurrentDispatcher?.current;
57
- }
58
- }
59
-
60
46
  type QueryType<T> =
61
47
  T extends Query<infer V, infer D, infer RR>
62
48
  ? {
63
49
  variables: V,
64
50
  response: D,
65
51
  rawResponse?: $NonMaybeType<RR>,
66
- } // $FlowFixMe[deprecated-type]
67
- : $Call<<T>(PreloadableConcreteRequest<T>) => T, T>;
52
+ }
53
+ : [+t: T] extends [+t: PreloadableConcreteRequest<infer V>]
54
+ ? V
55
+ : empty;
68
56
 
69
57
  declare function loadQuery<
70
58
  T,
@@ -87,17 +75,6 @@ function loadQuery<
87
75
  options?: ?LoadQueryOptions,
88
76
  environmentProviderOptions?: ?TEnvironmentProviderOptions,
89
77
  ): PreloadedQueryInner<TQuery, TEnvironmentProviderOptions> {
90
- // This code ensures that we don't call loadQuery during render.
91
- const CurrentDispatcher =
92
- // $FlowFixMe[prop-missing]
93
- React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
94
- ?.ReactCurrentDispatcher?.current;
95
- warning(
96
- RenderDispatcher == null || CurrentDispatcher !== RenderDispatcher,
97
- 'Relay: `%s` should not be called inside a React render function.',
98
- options?.__nameForWarning ?? 'loadQuery',
99
- );
100
-
101
78
  // Every time you call loadQuery, we will generate a new fetchKey.
102
79
  // This will ensure that every query reference that is created and
103
80
  // passed to usePreloadedQuery is independently evaluated,
@@ -407,5 +384,4 @@ function loadQuery<
407
384
 
408
385
  module.exports = {
409
386
  loadQuery,
410
- useTrackLoadQueryInRender,
411
387
  };
@@ -16,13 +16,15 @@ import type {
16
16
  EntryPointComponent,
17
17
  EnvironmentProviderOptions,
18
18
  IEnvironmentProvider,
19
+ PreloadedQuery,
19
20
  } from './EntryPointTypes.flow';
20
21
 
21
22
  const preloadQuery = require('./preloadQuery_DEPRECATED');
22
23
 
23
24
  function prepareEntryPoint<
24
25
  TEntryPointParams: {...},
25
- TPreloadedQueries: {...},
26
+ // $FlowExpectedError[unclear-type] Need any to make it supertype of all PreloadedQuery
27
+ TPreloadedQueries: {+[string]: PreloadedQuery<any>},
26
28
  TPreloadedEntryPoints: {...},
27
29
  TRuntimeProps: {...},
28
30
  TExtraProps,
@@ -59,6 +61,7 @@ function prepareEntryPoint<
59
61
  environmentProviderOptions,
60
62
  );
61
63
 
64
+ // $FlowFixMe[incompatible-type]
62
65
  preloadedQueries[queryPropName] = preloadQuery<OperationType, mixed>(
63
66
  environment,
64
67
  parameters,
@@ -85,20 +85,11 @@ function handlePotentialSnapshotErrorsForState(
85
85
  if (state.kind === 'singular') {
86
86
  handlePotentialSnapshotErrors(
87
87
  environment,
88
- state.snapshot.missingRequiredFields,
89
- state.snapshot.relayResolverErrors,
90
88
  state.snapshot.errorResponseFields,
91
- state.snapshot.selector.node.metadata?.throwOnFieldError ?? false,
92
89
  );
93
90
  } else if (state.kind === 'plural') {
94
91
  for (const snapshot of state.snapshots) {
95
- handlePotentialSnapshotErrors(
96
- environment,
97
- snapshot.missingRequiredFields,
98
- snapshot.relayResolverErrors,
99
- snapshot.errorResponseFields,
100
- snapshot.selector.node.metadata?.throwOnFieldError ?? false,
101
- );
92
+ handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
102
93
  }
103
94
  }
104
95
  }
@@ -17,10 +17,10 @@ import type {
17
17
  EnvironmentProviderOptions,
18
18
  IEnvironmentProvider,
19
19
  PreloadedEntryPoint,
20
+ PreloadedQuery,
20
21
  } from './EntryPointTypes.flow';
21
22
 
22
23
  const loadEntryPoint = require('./loadEntryPoint');
23
- const {useTrackLoadQueryInRender} = require('./loadQuery');
24
24
  const useIsMountedRef = require('./useIsMountedRef');
25
25
  const {useCallback, useEffect, useRef, useState} = require('react');
26
26
 
@@ -52,7 +52,8 @@ const initialNullEntryPointReferenceState = {kind: 'NullEntryPointReference'};
52
52
 
53
53
  hook useLoadEntryPoint<
54
54
  TEntryPointParams: {...},
55
- TPreloadedQueries: {...},
55
+ // $FlowExpectedError[unclear-type] Need any to make it supertype of all PreloadedQuery
56
+ TPreloadedQueries: {+[string]: PreloadedQuery<any>},
56
57
  TPreloadedEntryPoints: {...},
57
58
  TRuntimeProps: {...},
58
59
  TExtraProps,
@@ -102,8 +103,6 @@ hook useLoadEntryPoint<
102
103
  * entry point references.
103
104
  */
104
105
 
105
- useTrackLoadQueryInRender();
106
-
107
106
  const initialEntryPointReferenceInternal =
108
107
  options?.TEST_ONLY__initialEntryPointData?.entryPointReference ??
109
108
  initialNullEntryPointReferenceState;
@@ -13,7 +13,6 @@
13
13
 
14
14
  import type {Fragment, FragmentType, GraphQLTaggedNode} from 'relay-runtime';
15
15
 
16
- const {useTrackLoadQueryInRender} = require('./loadQuery');
17
16
  const useFragmentInternal = require('./useFragmentInternal');
18
17
  const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
19
18
  const {useDebugValue} = require('react');
@@ -49,10 +48,6 @@ declare hook useFragment<TFragmentType: FragmentType, TData>(
49
48
  ): ?TData;
50
49
 
51
50
  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
-
56
51
  const fragmentNode = getFragment(fragment);
57
52
  useStaticFragmentNodeWarning(fragmentNode, 'first argument of useFragment()');
58
53
  const data = useFragmentInternal(fragmentNode, key, 'useFragment()');