use-subscription.development.js
4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/** @license React vundefined
* use-subscription.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
if (process.env.NODE_ENV !== "production") {
(function() {
'use strict';
var _assign = require('object-assign');
var react = require('react');
//
// In order to avoid removing and re-adding subscriptions each time this hook is called,
// the parameters passed to this hook should be memoized in some way–
// either by wrapping the entire params object with useMemo()
// or by wrapping the individual callbacks with useCallback().
function useSubscription(_ref) {
var getCurrentValue = _ref.getCurrentValue,
subscribe = _ref.subscribe;
// Read the current value from our subscription.
// When this value changes, we'll schedule an update with React.
// It's important to also store the hook params so that we can check for staleness.
// (See the comment in checkForUpdates() below for more info.)
var _useState = react.useState(function () {
return {
getCurrentValue: getCurrentValue,
subscribe: subscribe,
value: getCurrentValue()
};
}),
state = _useState[0],
setState = _useState[1];
var valueToReturn = state.value; // If parameters have changed since our last render, schedule an update with its current value.
if (state.getCurrentValue !== getCurrentValue || state.subscribe !== subscribe) {
// If the subscription has been updated, we'll schedule another update with React.
// React will process this update immediately, so the old subscription value won't be committed.
// It is still nice to avoid returning a mismatched value though, so let's override the return value.
valueToReturn = getCurrentValue();
setState({
getCurrentValue: getCurrentValue,
subscribe: subscribe,
value: valueToReturn
});
} // Display the current value for this hook in React DevTools.
react.useDebugValue(valueToReturn); // It is important not to subscribe while rendering because this can lead to memory leaks.
// (Learn more at reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects)
// Instead, we wait until the commit phase to attach our handler.
//
// We intentionally use a passive effect (useEffect) rather than a synchronous one (useLayoutEffect)
// so that we don't stretch the commit phase.
// This also has an added benefit when multiple components are subscribed to the same source:
// It allows each of the event handlers to safely schedule work without potentially removing an another handler.
// (Learn more at https://codesandbox.io/s/k0yvr5970o)
react.useEffect(function () {
var didUnsubscribe = false;
var checkForUpdates = function () {
// It's possible that this callback will be invoked even after being unsubscribed,
// if it's removed as a result of a subscription event/update.
// In this case, React will log a DEV warning about an update from an unmounted component.
// We can avoid triggering that warning with this check.
if (didUnsubscribe) {
return;
} // We use a state updater function to avoid scheduling work for a stale source.
// However it's important to eagerly read the currently value,
// so that all scheduled work shares the same value (in the event of multiple subscriptions).
// This avoids visual "tearing" when a mutation happens during a (concurrent) render.
var value = getCurrentValue();
setState(function (prevState) {
// Ignore values from stale sources!
// Since we subscribe an unsubscribe in a passive effect,
// it's possible that this callback will be invoked for a stale (previous) subscription.
// This check avoids scheduling an update for that stale subscription.
if (prevState.getCurrentValue !== getCurrentValue || prevState.subscribe !== subscribe) {
return prevState;
} // Some subscriptions will auto-invoke the handler, even if the value hasn't changed.
// If the value hasn't changed, no update is needed.
// Return state as-is so React can bail out and avoid an unnecessary render.
if (prevState.value === value) {
return prevState;
}
return _assign({}, prevState, {
value: value
});
});
};
var unsubscribe = subscribe(checkForUpdates); // Because we're subscribing in a passive effect,
// it's possible that an update has occurred between render and our effect handler.
// Check for this and schedule an update if work has occurred.
checkForUpdates();
return function () {
didUnsubscribe = true;
unsubscribe();
};
}, [getCurrentValue, subscribe]); // Return the current value for our caller to use while rendering.
return valueToReturn;
}
exports.useSubscription = useSubscription;
})();
}