UNPKG

10.9 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its 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 * @format
9 */
10// flowlint ambiguous-object-type:error
11'use strict';
12
13var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
14
15var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
16
17var invariant = require("fbjs/lib/invariant");
18
19var _require = require('relay-runtime'),
20 createOperationDescriptor = _require.createOperationDescriptor,
21 isRelayModernEnvironment = _require.isRelayModernEnvironment,
22 fetchQuery = _require.__internal.fetchQuery;
23
24var ReactRelayQueryFetcher = /*#__PURE__*/function () {
25 function ReactRelayQueryFetcher(args) {
26 (0, _defineProperty2["default"])(this, "_selectionReferences", []);
27 (0, _defineProperty2["default"])(this, "_callOnDataChangeWhenSet", false);
28
29 if (args != null) {
30 this._cacheSelectionReference = args.cacheSelectionReference;
31 this._selectionReferences = args.selectionReferences;
32 }
33 }
34
35 var _proto = ReactRelayQueryFetcher.prototype;
36
37 _proto.getSelectionReferences = function getSelectionReferences() {
38 return {
39 cacheSelectionReference: this._cacheSelectionReference,
40 selectionReferences: this._selectionReferences
41 };
42 };
43
44 _proto.lookupInStore = function lookupInStore(environment, operation, fetchPolicy) {
45 if (fetchPolicy === 'store-and-network' || fetchPolicy === 'store-or-network') {
46 if (environment.check(operation).status === 'available') {
47 this._retainCachedOperation(environment, operation);
48
49 return environment.lookup(operation.fragment);
50 }
51 }
52
53 return null;
54 };
55
56 _proto.execute = function execute(_ref) {
57 var _this = this;
58
59 var environment = _ref.environment,
60 operation = _ref.operation,
61 _ref$preservePrevious = _ref.preservePreviousReferences,
62 preservePreviousReferences = _ref$preservePrevious === void 0 ? false : _ref$preservePrevious;
63 var reference = environment.retain(operation);
64
65 var error = function error() {
66 // We may have partially fulfilled the request, so let the next request
67 // or the unmount dispose of the references.
68 _this._selectionReferences = _this._selectionReferences.concat(reference);
69 };
70
71 var complete = function complete() {
72 if (!preservePreviousReferences) {
73 _this.disposeSelectionReferences();
74 }
75
76 _this._selectionReferences = _this._selectionReferences.concat(reference);
77 };
78
79 var unsubscribe = function unsubscribe() {
80 // Let the next request or the unmount code dispose of the references.
81 // We may have partially fulfilled the request.
82 _this._selectionReferences = _this._selectionReferences.concat(reference);
83 };
84
85 if (!isRelayModernEnvironment(environment)) {
86 return environment.execute({
87 operation: operation
88 })["do"]({
89 error: error,
90 complete: complete,
91 unsubscribe: unsubscribe
92 });
93 }
94
95 return fetchQuery(environment, operation)["do"]({
96 error: error,
97 complete: complete,
98 unsubscribe: unsubscribe
99 });
100 };
101
102 _proto.setOnDataChange = function setOnDataChange(onDataChange) {
103 !this._fetchOptions ? process.env.NODE_ENV !== "production" ? invariant(false, 'ReactRelayQueryFetcher: `setOnDataChange` should have been called after having called `fetch`') : invariant(false) : void 0;
104
105 if (typeof onDataChange === 'function') {
106 // Mutate the most recent fetchOptions in place,
107 // So that in-progress requests can access the updated callback.
108 this._fetchOptions.onDataChangeCallbacks = this._fetchOptions.onDataChangeCallbacks || [];
109
110 this._fetchOptions.onDataChangeCallbacks.push(onDataChange);
111
112 if (this._callOnDataChangeWhenSet) {
113 // We don't reset '_callOnDataChangeWhenSet' because another callback may be set
114 if (this._error != null) {
115 onDataChange({
116 error: this._error
117 });
118 } else if (this._snapshot != null) {
119 onDataChange({
120 snapshot: this._snapshot
121 });
122 }
123 }
124 }
125 }
126 /**
127 * `fetch` fetches the data for the given operation.
128 * If a result is immediately available synchronously, it will be synchronously
129 * returned by this function.
130 *
131 * Otherwise, the fetched result will be communicated via the `onDataChange` callback.
132 * `onDataChange` will be called with the first result (**if it wasn't returned synchronously**),
133 * and then subsequently whenever the data changes.
134 */
135 ;
136
137 _proto.fetch = function fetch(fetchOptions, cacheConfigOverride) {
138 var _this2 = this;
139
140 var environment = fetchOptions.environment,
141 operation = fetchOptions.operation,
142 onDataChange = fetchOptions.onDataChange;
143 var fetchHasReturned = false;
144
145 var _error;
146
147 this.disposeRequest();
148 var oldOnDataChangeCallbacks = this._fetchOptions && this._fetchOptions.onDataChangeCallbacks;
149 this._fetchOptions = {
150 environment: environment,
151 onDataChangeCallbacks: oldOnDataChangeCallbacks || [],
152 operation: operation
153 };
154
155 if (onDataChange && this._fetchOptions.onDataChangeCallbacks.indexOf(onDataChange) === -1) {
156 this._fetchOptions.onDataChangeCallbacks.push(onDataChange);
157 }
158
159 var operationOverride = cacheConfigOverride ? createOperationDescriptor(operation.request.node, operation.request.variables, cacheConfigOverride) : operation;
160 var request = this.execute({
161 environment: environment,
162 operation: operationOverride
163 })["finally"](function () {
164 _this2._pendingRequest = null;
165 }).subscribe({
166 next: function next() {
167 // If we received a response,
168 // Make a note that to notify the callback when it's later added.
169 _this2._callOnDataChangeWhenSet = true;
170 _this2._error = null; // Only notify of the first result if `next` is being called **asynchronously**
171 // (i.e. after `fetch` has returned).
172
173 _this2._onQueryDataAvailable({
174 notifyFirstResult: fetchHasReturned
175 });
176 },
177 error: function error(err) {
178 // If we received a response when we didn't have a change callback,
179 // Make a note that to notify the callback when it's later added.
180 _this2._callOnDataChangeWhenSet = true;
181 _this2._error = err;
182 _this2._snapshot = null;
183 var onDataChangeCallbacks = _this2._fetchOptions && _this2._fetchOptions.onDataChangeCallbacks; // Only notify of error if `error` is being called **asynchronously**
184 // (i.e. after `fetch` has returned).
185
186 if (fetchHasReturned) {
187 if (onDataChangeCallbacks) {
188 onDataChangeCallbacks.forEach(function (onDataChange) {
189 onDataChange({
190 error: err
191 });
192 });
193 }
194 } else {
195 _error = err;
196 }
197 }
198 });
199 this._pendingRequest = {
200 dispose: function dispose() {
201 request.unsubscribe();
202 }
203 };
204 fetchHasReturned = true;
205
206 if (_error) {
207 throw _error;
208 }
209
210 return this._snapshot;
211 };
212
213 _proto.retry = function retry(cacheConfigOverride) {
214 !this._fetchOptions ? process.env.NODE_ENV !== "production" ? invariant(false, 'ReactRelayQueryFetcher: `retry` should be called after having called `fetch`') : invariant(false) : void 0;
215 return this.fetch({
216 environment: this._fetchOptions.environment,
217 operation: this._fetchOptions.operation,
218 onDataChange: null // If there are onDataChangeCallbacks they will be reused
219
220 }, cacheConfigOverride);
221 };
222
223 _proto.dispose = function dispose() {
224 this.disposeRequest();
225 this.disposeSelectionReferences();
226 };
227
228 _proto.disposeRequest = function disposeRequest() {
229 this._error = null;
230 this._snapshot = null; // order is important, dispose of pendingFetch before selectionReferences
231
232 if (this._pendingRequest) {
233 this._pendingRequest.dispose();
234 }
235
236 if (this._rootSubscription) {
237 this._rootSubscription.dispose();
238
239 this._rootSubscription = null;
240 }
241 };
242
243 _proto._retainCachedOperation = function _retainCachedOperation(environment, operation) {
244 this._disposeCacheSelectionReference();
245
246 this._cacheSelectionReference = environment.retain(operation);
247 };
248
249 _proto._disposeCacheSelectionReference = function _disposeCacheSelectionReference() {
250 this._cacheSelectionReference && this._cacheSelectionReference.dispose();
251 this._cacheSelectionReference = null;
252 };
253
254 _proto.disposeSelectionReferences = function disposeSelectionReferences() {
255 this._disposeCacheSelectionReference();
256
257 this._selectionReferences.forEach(function (r) {
258 return r.dispose();
259 });
260
261 this._selectionReferences = [];
262 };
263
264 _proto._onQueryDataAvailable = function _onQueryDataAvailable(_ref2) {
265 var _this3 = this;
266
267 var notifyFirstResult = _ref2.notifyFirstResult;
268 !this._fetchOptions ? process.env.NODE_ENV !== "production" ? invariant(false, 'ReactRelayQueryFetcher: `_onQueryDataAvailable` should have been called after having called `fetch`') : invariant(false) : void 0;
269 var _this$_fetchOptions = this._fetchOptions,
270 environment = _this$_fetchOptions.environment,
271 onDataChangeCallbacks = _this$_fetchOptions.onDataChangeCallbacks,
272 operation = _this$_fetchOptions.operation; // `_onQueryDataAvailable` can be called synchronously the first time and can be called
273 // multiple times by network layers that support data subscriptions.
274 // Wait until the first payload to call `onDataChange` and subscribe for data updates.
275
276 if (this._snapshot) {
277 return;
278 }
279
280 this._snapshot = environment.lookup(operation.fragment); // Subscribe to changes in the data of the root fragment
281
282 this._rootSubscription = environment.subscribe(this._snapshot, function (snapshot) {
283 // Read from this._fetchOptions in case onDataChange() was lazily added.
284 if (_this3._fetchOptions != null) {
285 var maybeNewOnDataChangeCallbacks = _this3._fetchOptions.onDataChangeCallbacks;
286
287 if (Array.isArray(maybeNewOnDataChangeCallbacks)) {
288 maybeNewOnDataChangeCallbacks.forEach(function (onDataChange) {
289 return onDataChange({
290 snapshot: snapshot
291 });
292 });
293 }
294 }
295 });
296
297 if (this._snapshot && notifyFirstResult && Array.isArray(onDataChangeCallbacks)) {
298 var snapshot = this._snapshot;
299 onDataChangeCallbacks.forEach(function (onDataChange) {
300 return onDataChange({
301 snapshot: snapshot
302 });
303 });
304 }
305 };
306
307 return ReactRelayQueryFetcher;
308}();
309
310module.exports = ReactRelayQueryFetcher;
\No newline at end of file