mobx-react-lite
Version:
Lightweight React bindings for MobX based on React 16.8+ and Hooks
133 lines • 5.69 kB
JavaScript
;
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useObserver = void 0;
var mobx_1 = require("mobx");
var react_1 = __importDefault(require("react"));
var printDebugValue_1 = require("./utils/printDebugValue");
var reactionCleanupTracking_1 = require("./utils/reactionCleanupTracking");
var staticRendering_1 = require("./staticRendering");
function observerComponentNameFor(baseComponentName) {
return "observer".concat(baseComponentName);
}
/**
* We use class to make it easier to detect in heap snapshots by name
*/
var ObjectToBeRetainedByReact = /** @class */ (function () {
function ObjectToBeRetainedByReact() {
}
return ObjectToBeRetainedByReact;
}());
function objectToBeRetainedByReactFactory() {
return new ObjectToBeRetainedByReact();
}
function useObserver(fn, baseComponentName) {
if (baseComponentName === void 0) { baseComponentName = "observed"; }
if ((0, staticRendering_1.isUsingStaticRendering)()) {
return fn();
}
var _a = __read(react_1.default.useState(objectToBeRetainedByReactFactory), 1), objectRetainedByReact = _a[0];
// Force update, see #2982
var _b = __read(react_1.default.useState(), 2), setState = _b[1];
var forceUpdate = function () { return setState([]); };
// StrictMode/ConcurrentMode/Suspense may mean that our component is
// rendered and abandoned multiple times, so we need to track leaked
// Reactions.
var reactionTrackingRef = react_1.default.useRef(null);
if (!reactionTrackingRef.current) {
// First render for this component (or first time since a previous
// reaction from an abandoned render was disposed).
var newReaction = new mobx_1.Reaction(observerComponentNameFor(baseComponentName), function () {
// Observable has changed, meaning we want to re-render
// BUT if we're a component that hasn't yet got to the useEffect()
// stage, we might be a component that _started_ to render, but
// got dropped, and we don't want to make state changes then.
// (It triggers warnings in StrictMode, for a start.)
if (trackingData_1.mounted) {
// We have reached useEffect(), so we're mounted, and can trigger an update
forceUpdate();
}
else {
// We haven't yet reached useEffect(), so we'll need to trigger a re-render
// when (and if) useEffect() arrives.
trackingData_1.changedBeforeMount = true;
}
});
var trackingData_1 = (0, reactionCleanupTracking_1.addReactionToTrack)(reactionTrackingRef, newReaction, objectRetainedByReact);
}
var reaction = reactionTrackingRef.current.reaction;
react_1.default.useDebugValue(reaction, printDebugValue_1.printDebugValue);
react_1.default.useEffect(function () {
// Called on first mount only
(0, reactionCleanupTracking_1.recordReactionAsCommitted)(reactionTrackingRef);
if (reactionTrackingRef.current) {
// Great. We've already got our reaction from our render;
// all we need to do is to record that it's now mounted,
// to allow future observable changes to trigger re-renders
reactionTrackingRef.current.mounted = true;
// Got a change before first mount, force an update
if (reactionTrackingRef.current.changedBeforeMount) {
reactionTrackingRef.current.changedBeforeMount = false;
forceUpdate();
}
}
else {
// The reaction we set up in our render has been disposed.
// This can be due to bad timings of renderings, e.g. our
// component was paused for a _very_ long time, and our
// reaction got cleaned up
// Re-create the reaction
reactionTrackingRef.current = {
reaction: new mobx_1.Reaction(observerComponentNameFor(baseComponentName), function () {
// We've definitely already been mounted at this point
forceUpdate();
}),
mounted: true,
changedBeforeMount: false,
cleanAt: Infinity
};
forceUpdate();
}
return function () {
reactionTrackingRef.current.reaction.dispose();
reactionTrackingRef.current = null;
};
}, []);
// render the original component, but have the
// reaction track the observables, so that rendering
// can be invalidated (see above) once a dependency changes
var rendering;
var exception;
reaction.track(function () {
try {
rendering = fn();
}
catch (e) {
exception = e;
}
});
if (exception) {
throw exception; // re-throw any exceptions caught during rendering
}
return rendering;
}
exports.useObserver = useObserver;
//# sourceMappingURL=useObserver.js.map