UNPKG

6.79 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 * @emails oncall+relay
8 *
9 * @format
10 */
11// flowlint ambiguous-object-type:error
12'use strict';
13
14function _createForOfIteratorHelper(o) { if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var it, normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
15
16function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
17
18function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
19
20var useIsMountedRef = require('./useIsMountedRef');
21
22var useRelayEnvironment = require('./useRelayEnvironment');
23
24var _require = require('./loadQuery'),
25 loadQuery = _require.loadQuery,
26 useTrackLoadQueryInRender = _require.useTrackLoadQueryInRender;
27
28var _require2 = require('react'),
29 useCallback = _require2.useCallback,
30 useEffect = _require2.useEffect,
31 useRef = _require2.useRef,
32 useState = _require2.useState;
33
34var initialNullQueryReferenceState = {
35 kind: 'NullQueryReference'
36};
37
38function useLoadQuery(preloadableRequest) {
39 /**
40 * We want to always call `queryReference.dispose()` for every call to
41 * `setQueryReference(loadQuery(...))` so that no leaks of data in Relay stores
42 * will occur.
43 *
44 * However, a call to `setState(newState)` is not always followed by a commit where
45 * this value is reflected in the state. Thus, we cannot reliably clean up each
46 * ref with `useEffect(() => () => queryReference.dispose(), [queryReference])`.
47 *
48 * Instead, we keep track of each call to `loadQuery` in a ref.
49 * Relying on the fact that if a state change commits, no state changes that were
50 * initiated prior to the currently committing state change will ever subsequently
51 * commit, we can safely dispose of all preloaded query references
52 * associated with state changes initiated prior to the currently committing state
53 * change.
54 *
55 * Finally, when the hook unmounts, we also dispose of all remaining uncommitted
56 * query references.
57 */
58 var environment = useRelayEnvironment();
59 useTrackLoadQueryInRender();
60 var isMountedRef = useIsMountedRef();
61 var undisposedQueryReferencesRef = useRef(new Set([initialNullQueryReferenceState]));
62
63 var _useState = useState(initialNullQueryReferenceState),
64 queryReference = _useState[0],
65 setQueryReference = _useState[1];
66
67 var disposeQuery = useCallback(function () {
68 if (isMountedRef.current) {
69 var nullQueryReference = {
70 kind: 'NullQueryReference'
71 };
72 undisposedQueryReferencesRef.current.add(nullQueryReference);
73 setQueryReference(nullQueryReference);
74 }
75 }, [setQueryReference, isMountedRef]);
76 useEffect(function ensureQueryReferenceDisposal() {
77 // We are relying on the fact that sets iterate in insertion order, and we
78 // can remove items from a set as we iterate over it (i.e. no iterator
79 // invalidation issues.) Thus, it is safe to loop through
80 // undisposedQueryReferences until we find queryReference, and
81 // remove and dispose all previous references.
82 //
83 // We are guaranteed to find queryReference in the set, because if a
84 // state change results in a commit, no state changes initiated prior to that
85 // one will be committed, and we are disposing and removing references
86 // associated with commits that were initiated prior to the currently
87 // committing state change. (A useEffect callback is called during the commit
88 // phase.)
89 var undisposedQueryReferences = undisposedQueryReferencesRef.current;
90
91 if (isMountedRef.current) {
92 var _iterator = _createForOfIteratorHelper(undisposedQueryReferences),
93 _step;
94
95 try {
96 for (_iterator.s(); !(_step = _iterator.n()).done;) {
97 var undisposedQueryReference = _step.value;
98
99 if (undisposedQueryReference === queryReference) {
100 break;
101 }
102
103 undisposedQueryReferences["delete"](undisposedQueryReference);
104
105 if (undisposedQueryReference.kind !== 'NullQueryReference') {
106 undisposedQueryReference.dispose();
107 }
108 }
109 } catch (err) {
110 _iterator.e(err);
111 } finally {
112 _iterator.f();
113 }
114 }
115 }, [queryReference, isMountedRef]);
116 useEffect(function () {
117 return function disposeAllRemainingQueryReferences() {
118 // undisposedQueryReferences.current is never reassigned
119 // eslint-disable-next-line react-hooks/exhaustive-deps
120 var _iterator2 = _createForOfIteratorHelper(undisposedQueryReferencesRef.current),
121 _step2;
122
123 try {
124 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
125 var unhandledStateChange = _step2.value;
126
127 if (unhandledStateChange.kind !== 'NullQueryReference') {
128 unhandledStateChange.dispose();
129 }
130 }
131 } catch (err) {
132 _iterator2.e(err);
133 } finally {
134 _iterator2.f();
135 }
136 };
137 }, []);
138 var queryLoaderCallback = useCallback(function (variables, options) {
139 if (isMountedRef.current) {
140 var updatedQueryReference = loadQuery(environment, preloadableRequest, variables, options);
141 undisposedQueryReferencesRef.current.add(updatedQueryReference);
142 setQueryReference(updatedQueryReference);
143 }
144 }, [environment, preloadableRequest, setQueryReference, isMountedRef]);
145 return [queryReference.kind === 'NullQueryReference' ? null : queryReference, queryLoaderCallback, disposeQuery];
146}
147
148module.exports = useLoadQuery;
\No newline at end of file