react-relay 13.0.3 → 13.2.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 (42) hide show
  1. package/ReactRelayContext.js +1 -1
  2. package/ReactRelayLocalQueryRenderer.js.flow +1 -1
  3. package/ReactRelayQueryRenderer.js.flow +1 -4
  4. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +1 -3
  5. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +1 -3
  6. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +2 -4
  7. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +1 -3
  8. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +2 -4
  9. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +1 -3
  10. package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +1 -3
  11. package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +1 -3
  12. package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +1 -3
  13. package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +1 -3
  14. package/hooks.js +1 -1
  15. package/index.js +1 -1
  16. package/jest-react/internalAct.js.flow +25 -9
  17. package/legacy.js +1 -1
  18. package/lib/ReactRelayQueryRenderer.js +1 -1
  19. package/lib/jest-react/internalAct.js +24 -4
  20. package/lib/relay-hooks/FragmentResource.js +10 -13
  21. package/lib/relay-hooks/QueryResource.js +2 -165
  22. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +7 -11
  23. package/lib/relay-hooks/react-cache/RelayReactCache.js +37 -0
  24. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +197 -0
  25. package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +395 -0
  26. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +45 -0
  27. package/package.json +3 -3
  28. package/react-relay-hooks.js +2 -2
  29. package/react-relay-hooks.min.js +2 -2
  30. package/react-relay-legacy.js +2 -2
  31. package/react-relay-legacy.min.js +2 -2
  32. package/react-relay.js +2 -2
  33. package/react-relay.min.js +2 -2
  34. package/relay-hooks/FragmentResource.js.flow +17 -18
  35. package/relay-hooks/QueryResource.js.flow +4 -201
  36. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +1 -3
  37. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +1 -3
  38. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +7 -14
  39. package/relay-hooks/react-cache/RelayReactCache.js.flow +42 -0
  40. package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +243 -0
  41. package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +416 -0
  42. package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +66 -0
@@ -24,7 +24,6 @@ var SuspenseResource = require('./SuspenseResource');
24
24
  var invariant = require('invariant');
25
25
 
26
26
  var _require = require('relay-runtime'),
27
- RelayFeatureFlags = _require.RelayFeatureFlags,
28
27
  isPromise = _require.isPromise;
29
28
 
30
29
  var warning = require("fbjs/lib/warning");
@@ -66,18 +65,6 @@ function getQueryResult(operation, cacheIdentifier) {
66
65
  var nextID = 200000;
67
66
 
68
67
  function createCacheEntry(cacheIdentifier, operation, operationAvailability, value, networkSubscription, onDispose) {
69
- // There should be no behavior difference between createCacheEntry_new and
70
- // createCacheEntry_old, and it doesn't directly relate to Client Edges.
71
- // It was just a refactoring that was needed for Client Edges but that
72
- // is behind the feature flag just in case there is any accidental breakage.
73
- if (RelayFeatureFlags.REFACTOR_SUSPENSE_RESOURCE) {
74
- return createCacheEntry_new(cacheIdentifier, operation, operationAvailability, value, networkSubscription, onDispose);
75
- } else {
76
- return createCacheEntry_old(cacheIdentifier, operation, operationAvailability, value, networkSubscription, onDispose);
77
- }
78
- }
79
-
80
- function createCacheEntry_new(cacheIdentifier, operation, operationAvailability, value, networkSubscription, onDispose) {
81
68
  var isLiveQuery = operationIsLiveQuery(operation);
82
69
  var currentValue = value;
83
70
  var currentNetworkSubscription = networkSubscription;
@@ -108,12 +95,6 @@ function createCacheEntry_new(cacheIdentifier, operation, operationAvailability,
108
95
  setValue: function setValue(val) {
109
96
  currentValue = val;
110
97
  },
111
- getRetainCount: function getRetainCount() {
112
- return suspenseResource.getRetainCount();
113
- },
114
- getNetworkSubscription: function getNetworkSubscription() {
115
- return currentNetworkSubscription;
116
- },
117
98
  setNetworkSubscription: function setNetworkSubscription(subscription) {
118
99
  if (isLiveQuery && currentNetworkSubscription != null) {
119
100
  currentNetworkSubscription.unsubscribe();
@@ -134,156 +115,12 @@ function createCacheEntry_new(cacheIdentifier, operation, operationAvailability,
134
115
  return cacheEntry;
135
116
  }
136
117
 
137
- var DATA_RETENTION_TIMEOUT = 5 * 60 * 1000;
138
-
139
- function createCacheEntry_old(cacheIdentifier, operation, operationAvailability, value, networkSubscription, onDispose) {
140
- var isLiveQuery = operationIsLiveQuery(operation);
141
- var currentValue = value;
142
- var retainCount = 0;
143
- var retainDisposable = null;
144
- var _releaseTemporaryRetain = null;
145
- var currentNetworkSubscription = networkSubscription;
146
-
147
- var retain = function retain(environment) {
148
- retainCount++;
149
-
150
- if (retainCount === 1) {
151
- retainDisposable = environment.retain(operation);
152
- }
153
-
154
- return {
155
- dispose: function dispose() {
156
- retainCount = Math.max(0, retainCount - 1);
157
-
158
- if (retainCount === 0) {
159
- !(retainDisposable != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected disposable to release query to be defined.' + "If you're seeing this, this is likely a bug in Relay.") : invariant(false) : void 0;
160
- retainDisposable.dispose();
161
- retainDisposable = null;
162
- }
163
-
164
- onDispose(cacheEntry);
165
- }
166
- };
167
- };
168
-
169
- var cacheEntry = {
170
- cacheIdentifier: cacheIdentifier,
171
- id: nextID++,
172
- processedPayloadsCount: 0,
173
- operationAvailability: operationAvailability,
174
- getValue: function getValue() {
175
- return currentValue;
176
- },
177
- setValue: function setValue(val) {
178
- currentValue = val;
179
- },
180
- getRetainCount: function getRetainCount() {
181
- return retainCount;
182
- },
183
- getNetworkSubscription: function getNetworkSubscription() {
184
- return currentNetworkSubscription;
185
- },
186
- setNetworkSubscription: function setNetworkSubscription(subscription) {
187
- if (isLiveQuery && currentNetworkSubscription != null) {
188
- currentNetworkSubscription.unsubscribe();
189
- }
190
-
191
- currentNetworkSubscription = subscription;
192
- },
193
- temporaryRetain: function temporaryRetain(environment) {
194
- // NOTE: If we're executing in a server environment, there's no need
195
- // to create temporary retains, since the component will never commit.
196
- if (environment.isServer()) {
197
- return {
198
- dispose: function dispose() {}
199
- };
200
- } // NOTE: temporaryRetain is called during the render phase. However,
201
- // given that we can't tell if this render will eventually commit or not,
202
- // we create a timer to autodispose of this retain in case the associated
203
- // component never commits.
204
- // If the component /does/ commit, permanentRetain will clear this timeout
205
- // and permanently retain the data.
206
-
207
-
208
- var disposable = retain(environment);
209
- var releaseQueryTimeout = null;
210
-
211
- var localReleaseTemporaryRetain = function localReleaseTemporaryRetain() {
212
- clearTimeout(releaseQueryTimeout);
213
- releaseQueryTimeout = null;
214
- _releaseTemporaryRetain = null;
215
- disposable.dispose(); // Normally if this entry never commits, the request would've ended by the
216
- // time this timeout expires and the temporary retain is released. However,
217
- // we need to do this for live queries which remain open indefinitely.
218
-
219
- if (isLiveQuery && retainCount <= 0 && currentNetworkSubscription != null) {
220
- currentNetworkSubscription.unsubscribe();
221
- }
222
- };
223
-
224
- releaseQueryTimeout = setTimeout(localReleaseTemporaryRetain, DATA_RETENTION_TIMEOUT); // NOTE: Since temporaryRetain can be called multiple times, we release
225
- // the previous temporary retain after we re-establish a new one, since
226
- // we only ever need a single temporary retain until the permanent retain is
227
- // established.
228
- // temporaryRetain may be called multiple times by React during the render
229
- // phase, as well as multiple times by other query components that are
230
- // rendering the same query/variables.
231
-
232
- if (_releaseTemporaryRetain != null) {
233
- _releaseTemporaryRetain();
234
- }
235
-
236
- _releaseTemporaryRetain = localReleaseTemporaryRetain;
237
- return {
238
- dispose: function dispose() {
239
- _releaseTemporaryRetain && _releaseTemporaryRetain();
240
- }
241
- };
242
- },
243
- permanentRetain: function permanentRetain(environment) {
244
- var disposable = retain(environment);
245
-
246
- if (_releaseTemporaryRetain != null) {
247
- _releaseTemporaryRetain();
248
-
249
- _releaseTemporaryRetain = null;
250
- }
251
-
252
- return {
253
- dispose: function dispose() {
254
- disposable.dispose();
255
-
256
- if (isLiveQuery && retainCount <= 0 && currentNetworkSubscription != null) {
257
- currentNetworkSubscription.unsubscribe();
258
- }
259
- }
260
- };
261
- },
262
- releaseTemporaryRetain: function releaseTemporaryRetain() {
263
- if (_releaseTemporaryRetain != null) {
264
- _releaseTemporaryRetain();
265
-
266
- _releaseTemporaryRetain = null;
267
- }
268
- }
269
- };
270
- return cacheEntry;
271
- }
272
-
273
118
  var QueryResourceImpl = /*#__PURE__*/function () {
274
119
  function QueryResourceImpl(environment) {
275
120
  var _this = this;
276
121
 
277
122
  (0, _defineProperty2["default"])(this, "_clearCacheEntry", function (cacheEntry) {
278
- // The new code does this retainCount <= 0 check within SuspenseResource
279
- // before calling _clearCacheEntry, whereas with the old code we do it here.
280
- if (RelayFeatureFlags.REFACTOR_SUSPENSE_RESOURCE) {
281
- _this._cache["delete"](cacheEntry.cacheIdentifier);
282
- } else {
283
- if (cacheEntry.getRetainCount() <= 0) {
284
- _this._cache["delete"](cacheEntry.cacheIdentifier);
285
- }
286
- }
123
+ _this._cache["delete"](cacheEntry.cacheIdentifier);
287
124
  });
288
125
  this._environment = environment;
289
126
  this._cache = LRUCache.create(CACHE_CAPACITY);
@@ -529,7 +366,7 @@ var QueryResourceImpl = /*#__PURE__*/function () {
529
366
  } else {
530
367
  // TODO:T92030819 Remove this warning and actually throw the network error
531
368
  // To complete this task we need to have a way of precisely tracking suspendable points
532
- process.env.NODE_ENV !== "production" ? warning(false, 'QueryResource: An incremental payload for query `%` returned an error: `%`:`%`.', operation.fragment.node.name, _error.message, _error.stack) : void 0;
369
+ process.env.NODE_ENV !== "production" ? warning(false, 'QueryResource: An incremental payload for query `%s` returned an error: `%s`.', operation.fragment.node.name, String(_error.message)) : void 0;
533
370
  }
534
371
 
535
372
  resolveNetworkPromise();
@@ -64,17 +64,13 @@ function preloadQuery(environment, preloadableRequest, variables, options, envir
64
64
  return;
65
65
  }
66
66
 
67
- if (RelayFeatureFlags.DELAY_CLEANUP_OF_PENDING_PRELOAD_QUERIES === true) {
68
- setTimeout(function () {
69
- // Clear the cache entry after the default timeout
70
- // null-check for Flow
71
- if (queryEntry != null) {
72
- cleanup(pendingQueries, queryEntry);
73
- }
74
- }, DEFAULT_PREFETCH_TIMEOUT);
75
- } else {
76
- cleanup(pendingQueries, queryEntry);
77
- }
67
+ setTimeout(function () {
68
+ // Clear the cache entry after the default timeout
69
+ // null-check for Flow
70
+ if (queryEntry != null) {
71
+ cleanup(pendingQueries, queryEntry);
72
+ }
73
+ }, DEFAULT_PREFETCH_TIMEOUT);
78
74
  };
79
75
  }) : null;
80
76
  return {
@@ -0,0 +1,37 @@
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
+ *
8
+ * @emails oncall+relay
9
+ * @format
10
+ */
11
+ // flowlint ambiguous-object-type:error
12
+ 'use strict';
13
+
14
+ var invariant = require('invariant'); // $FlowFixMe[prop-missing] These exist in experimental builds but aren't in React's types yet.
15
+
16
+
17
+ var _require = require('react'),
18
+ unstable_getCacheForType = _require.unstable_getCacheForType,
19
+ unstable_getCacheSignal = _require.unstable_getCacheSignal;
20
+
21
+ var _require2 = require('relay-runtime'),
22
+ RelayFeatureFlags = _require2.RelayFeatureFlags;
23
+
24
+ function getCacheForType(factory) {
25
+ !(typeof unstable_getCacheForType === 'function' && RelayFeatureFlags.USE_REACT_CACHE) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayReactCache.getCacheForType should only be called when the USE_REACT_CACHE feature flag is enabled and when on an experimental React build that supports it.') : invariant(false) : void 0;
26
+ return unstable_getCacheForType(factory);
27
+ }
28
+
29
+ function getCacheSignal() {
30
+ !(typeof unstable_getCacheSignal === 'function' && RelayFeatureFlags.USE_REACT_CACHE) ? process.env.NODE_ENV !== "production" ? invariant(false, 'RelayReactCache.getCacheSignal should only be called when the USE_REACT_CACHE feature flag is enabled and when on an experimental React build that supports it.') : invariant(false) : void 0;
31
+ return unstable_getCacheSignal();
32
+ }
33
+
34
+ module.exports = {
35
+ getCacheForType: getCacheForType,
36
+ getCacheSignal: getCacheSignal
37
+ };
@@ -0,0 +1,197 @@
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
+ *
8
+ * @emails oncall+relay
9
+ * @format
10
+ */
11
+ // flowlint ambiguous-object-type:error
12
+ 'use strict';
13
+
14
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
15
+
16
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
17
+
18
+ var _require = require('./RelayReactCache'),
19
+ getCacheForType = _require.getCacheForType,
20
+ getCacheSignal = _require.getCacheSignal;
21
+
22
+ var invariant = require('invariant');
23
+
24
+ var _require2 = require('relay-runtime'),
25
+ fetchQueryInternal = _require2.__internal.fetchQuery;
26
+
27
+ var warning = require("fbjs/lib/warning");
28
+
29
+ var DEFAULT_FETCH_POLICY = 'store-or-network';
30
+
31
+ function createQueryCache() {
32
+ return new Map();
33
+ }
34
+
35
+ function getQueryCacheKey(operation, fetchPolicy, renderPolicy) {
36
+ var cacheIdentifier = "".concat(fetchPolicy, "-").concat(renderPolicy, "-").concat(operation.request.identifier);
37
+ return cacheIdentifier;
38
+ }
39
+
40
+ function constructQueryResult(operation) {
41
+ var rootFragmentRef = {
42
+ __id: operation.fragment.dataID,
43
+ __fragments: (0, _defineProperty2["default"])({}, operation.fragment.node.name, operation.request.variables),
44
+ __fragmentOwner: operation.request
45
+ };
46
+ return {
47
+ fragmentNode: operation.request.node.fragment,
48
+ fragmentRef: rootFragmentRef
49
+ };
50
+ }
51
+
52
+ function getQueryResultOrFetchQuery_REACT_CACHE(environment, queryOperationDescriptor) {
53
+ var fetchPolicy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_FETCH_POLICY;
54
+ var maybeRenderPolicy = arguments.length > 3 ? arguments[3] : undefined;
55
+ var renderPolicy = maybeRenderPolicy !== null && maybeRenderPolicy !== void 0 ? maybeRenderPolicy : environment.UNSTABLE_getDefaultRenderPolicy();
56
+ var cache = getCacheForType(createQueryCache);
57
+ var cacheKey = getQueryCacheKey(queryOperationDescriptor, fetchPolicy, renderPolicy);
58
+ var entry = cache.get(cacheKey);
59
+
60
+ if (entry === undefined) {
61
+ // Initiate a query to fetch the data if needed:
62
+ entry = onCacheMiss(environment, queryOperationDescriptor, fetchPolicy, renderPolicy, function (newCacheEntry) {
63
+ cache.set(cacheKey, newCacheEntry);
64
+ });
65
+ cache.set(cacheKey, entry); // Since this is the first time rendering, retain the query. React will
66
+ // trigger the abort signal when this cache entry is no longer needed.
67
+
68
+ var retention = environment.retain(queryOperationDescriptor);
69
+ var abortSignal = getCacheSignal();
70
+ abortSignal.addEventListener('abort', function () {
71
+ retention.dispose();
72
+ cache["delete"](cacheKey);
73
+ }, {
74
+ once: true
75
+ });
76
+ }
77
+
78
+ switch (entry.status) {
79
+ case 'pending':
80
+ throw entry.promise;
81
+
82
+ case 'rejected':
83
+ throw entry.error;
84
+
85
+ case 'resolved':
86
+ return entry.result;
87
+ }
88
+
89
+ !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'switch statement should be exhaustive') : invariant(false) : void 0;
90
+ }
91
+
92
+ function onCacheMiss(environment, operation, fetchPolicy, renderPolicy, updateCache) {
93
+ // NB: Besides checking if the data is available, calling `check` will write missing
94
+ // data to the store using any missing data handlers specified in the environment.
95
+ var queryAvailability = environment.check(operation);
96
+ var queryStatus = queryAvailability.status;
97
+ var hasFullQuery = queryStatus === 'available';
98
+ var canPartialRender = hasFullQuery || renderPolicy === 'partial' && queryStatus !== 'stale';
99
+ var shouldFetch;
100
+ var shouldRenderNow;
101
+
102
+ switch (fetchPolicy) {
103
+ case 'store-only':
104
+ {
105
+ shouldFetch = false;
106
+ shouldRenderNow = true;
107
+ break;
108
+ }
109
+
110
+ case 'store-or-network':
111
+ {
112
+ shouldFetch = !hasFullQuery;
113
+ shouldRenderNow = canPartialRender;
114
+ break;
115
+ }
116
+
117
+ case 'store-and-network':
118
+ {
119
+ shouldFetch = true;
120
+ shouldRenderNow = canPartialRender;
121
+ break;
122
+ }
123
+
124
+ case 'network-only':
125
+ default:
126
+ {
127
+ shouldFetch = true;
128
+ shouldRenderNow = false;
129
+ break;
130
+ }
131
+ }
132
+
133
+ var promise = shouldFetch ? executeOperationAndKeepUpToDate(environment, operation, updateCache) : undefined;
134
+
135
+ if (shouldRenderNow) {
136
+ return {
137
+ status: 'resolved',
138
+ result: constructQueryResult(operation)
139
+ };
140
+ } else {
141
+ !promise ? process.env.NODE_ENV !== "production" ? invariant(false, 'Should either fetch or render (or both), otherwise we would suspend forever.') : invariant(false) : void 0;
142
+ return {
143
+ status: 'pending',
144
+ promise: promise
145
+ };
146
+ }
147
+ }
148
+
149
+ function executeOperationAndKeepUpToDate(environment, operation, updateCache) {
150
+ var resolvePromise;
151
+ var promise = new Promise(function (r) {
152
+ resolvePromise = r;
153
+ }); // $FlowExpectedError[prop-missing] Expando to annotate Promises.
154
+
155
+ promise.displayName = 'Relay(' + operation.request.node.operation.name + ')';
156
+ var isFirstPayload = true; // FIXME We may still need to cancel network requests for live queries.
157
+
158
+ var fetchObservable = fetchQueryInternal(environment, operation);
159
+ fetchObservable.subscribe({
160
+ start: function start(subscription) {},
161
+ error: function error(_error) {
162
+ if (isFirstPayload) {
163
+ updateCache({
164
+ status: 'rejected',
165
+ error: _error
166
+ });
167
+ } else {
168
+ // TODO:T92030819 Remove this warning and actually throw the network error
169
+ // To complete this task we need to have a way of precisely tracking suspendable points
170
+ process.env.NODE_ENV !== "production" ? warning(false, 'getQueryResultOrFetchQuery: An incremental payload for query `%` returned an error: `%`:`%`.', operation.request.node.operation.name, _error.message, _error.stack) : void 0;
171
+ }
172
+
173
+ resolvePromise();
174
+ isFirstPayload = false;
175
+ },
176
+ next: function next(response) {
177
+ // Stop suspending on the first payload because of streaming, defer, etc.
178
+ updateCache({
179
+ status: 'resolved',
180
+ result: constructQueryResult(operation)
181
+ });
182
+ resolvePromise();
183
+ isFirstPayload = false;
184
+ },
185
+ complete: function complete() {
186
+ updateCache({
187
+ status: 'resolved',
188
+ result: constructQueryResult(operation)
189
+ });
190
+ resolvePromise();
191
+ isFirstPayload = false;
192
+ }
193
+ });
194
+ return promise;
195
+ }
196
+
197
+ module.exports = getQueryResultOrFetchQuery_REACT_CACHE;