react-relay 17.0.0 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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()');