UNPKG

mobx-react-lite

Version:

Lightweight React bindings for MobX based on React 16.8+ and Hooks

133 lines 5.69 kB
"use strict"; 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