UNPKG

mobx-react-lite

Version:

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

126 lines 5.26 kB
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; }; import { Reaction } from "mobx"; import React from "react"; import { printDebugValue } from "./utils/printDebugValue"; import { addReactionToTrack, recordReactionAsCommitted } from "./utils/reactionCleanupTracking"; import { isUsingStaticRendering } from "./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(); } export function useObserver(fn, baseComponentName) { if (baseComponentName === void 0) { baseComponentName = "observed"; } if (isUsingStaticRendering()) { return fn(); } var _a = __read(React.useState(objectToBeRetainedByReactFactory), 1), objectRetainedByReact = _a[0]; // Force update, see #2982 var _b = __read(React.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.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 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 = addReactionToTrack(reactionTrackingRef, newReaction, objectRetainedByReact); } var reaction = reactionTrackingRef.current.reaction; React.useDebugValue(reaction, printDebugValue); React.useEffect(function () { // Called on first mount only 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 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; } //# sourceMappingURL=useObserver.js.map