UNPKG

6.98 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 loadEntryPoint = require('./loadEntryPoint');
21
22var useIsMountedRef = require('./useIsMountedRef');
23
24var _require = require('./loadQuery'),
25 useTrackLoadQueryInRender = _require.useTrackLoadQueryInRender;
26
27var _require2 = require('react'),
28 useCallback = _require2.useCallback,
29 useEffect = _require2.useEffect,
30 useRef = _require2.useRef,
31 useState = _require2.useState;
32
33var initialNullEntryPointReferenceState = {
34 kind: 'NullEntryPointReference'
35};
36
37function useLoadEntryPoint(environmentProvider, entryPoint) {
38 /**
39 * We want to always call `entryPointReference.dispose()` for every call to
40 * `setEntryPointReference(loadEntryPoint(...))` so that no leaks of data in Relay
41 * stores will occur.
42 *
43 * However, a call to `setState(newState)` is not always followed by a commit where
44 * this value is reflected in the state. Thus, we cannot reliably clean up each ref
45 * with `useEffect(() => () => entryPointReference.dispose(), [entryPointReference])`.
46 *
47 * Instead, we keep track of each call to `loadEntryPoint` in a ref.
48 * Relying on the fact that if a state change commits, no state changes that were
49 * initiated prior to the currently committing state change will ever subsequently
50 * commit, we can safely dispose of all preloaded entry point references
51 * associated with state changes initiated prior to the currently committing state
52 * change.
53 *
54 * Finally, when the hook unmounts, we also dispose of all remaining uncommitted
55 * entry point references.
56 */
57 useTrackLoadQueryInRender();
58 var isMountedRef = useIsMountedRef();
59 var undisposedEntryPointReferencesRef = useRef(new Set([initialNullEntryPointReferenceState]));
60
61 var _useState = useState(initialNullEntryPointReferenceState),
62 entryPointReference = _useState[0],
63 setEntryPointReference = _useState[1];
64
65 var disposeEntryPoint = useCallback(function () {
66 if (isMountedRef.current) {
67 var nullEntryPointReference = {
68 kind: 'NullEntryPointReference'
69 };
70 undisposedEntryPointReferencesRef.current.add(nullEntryPointReference);
71 setEntryPointReference(nullEntryPointReference);
72 }
73 }, [setEntryPointReference, isMountedRef]);
74 useEffect(function disposePriorEntryPointReferences() {
75 // We are relying on the fact that sets iterate in insertion order, and we
76 // can remove items from a set as we iterate over it (i.e. no iterator
77 // invalidation issues.) Thus, it is safe to loop through
78 // undisposedEntryPointReferences until we find entryPointReference, and
79 // remove and dispose all previous references.
80 //
81 // We are guaranteed to find entryPointReference in the set, because if a
82 // state change results in a commit, no state changes initiated prior to that
83 // one will be committed, and we are disposing and removing references
84 // associated with commits that were initiated prior to the currently
85 // committing state change. (A useEffect callback is called during the commit
86 // phase.)
87 var undisposedEntryPointReferences = undisposedEntryPointReferencesRef.current;
88
89 if (isMountedRef.current) {
90 var _iterator = _createForOfIteratorHelper(undisposedEntryPointReferences),
91 _step;
92
93 try {
94 for (_iterator.s(); !(_step = _iterator.n()).done;) {
95 var undisposedEntryPointReference = _step.value;
96
97 if (undisposedEntryPointReference === entryPointReference) {
98 break;
99 }
100
101 undisposedEntryPointReferences["delete"](undisposedEntryPointReference);
102
103 if (undisposedEntryPointReference.kind !== 'NullEntryPointReference') {
104 undisposedEntryPointReference.dispose();
105 }
106 }
107 } catch (err) {
108 _iterator.e(err);
109 } finally {
110 _iterator.f();
111 }
112 }
113 }, [entryPointReference, isMountedRef]);
114 useEffect(function () {
115 return function disposeAllRemainingEntryPointReferences() {
116 // undisposedEntryPointReferences.current is never reassigned
117 // eslint-disable-next-line react-hooks/exhaustive-deps
118 var _iterator2 = _createForOfIteratorHelper(undisposedEntryPointReferencesRef.current),
119 _step2;
120
121 try {
122 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
123 var unhandledStateChange = _step2.value;
124
125 if (unhandledStateChange.kind !== 'NullEntryPointReference') {
126 unhandledStateChange.dispose();
127 }
128 }
129 } catch (err) {
130 _iterator2.e(err);
131 } finally {
132 _iterator2.f();
133 }
134 };
135 }, []);
136 var entryPointLoaderCallback = useCallback(function (params) {
137 if (isMountedRef.current) {
138 var updatedEntryPointReference = loadEntryPoint(environmentProvider, entryPoint, params);
139 undisposedEntryPointReferencesRef.current.add(updatedEntryPointReference);
140 setEntryPointReference(updatedEntryPointReference);
141 }
142 }, [environmentProvider, entryPoint, setEntryPointReference, isMountedRef]);
143 return [entryPointReference.kind === 'NullEntryPointReference' ? null : entryPointReference, entryPointLoaderCallback, disposeEntryPoint];
144}
145
146module.exports = useLoadEntryPoint;
\No newline at end of file