mobx-react-lite
Version:
Lightweight React bindings for MobX based on React 16.8+ and Hooks
91 lines • 3.97 kB
JavaScript
import { Reaction } from "mobx";
import React from "react";
import { printDebugValue } from "./utils/printDebugValue";
import { isUsingStaticRendering } from "./staticRendering";
import { observerFinalizationRegistry } from "./utils/observerFinalizationRegistry";
import { useSyncExternalStore } from "use-sync-external-store/shim";
function createReaction(adm) {
adm.reaction = new Reaction("observer".concat(adm.name), function () {
var _a;
adm.stateVersion = Symbol();
// onStoreChange won't be available until the component "mounts".
// If state changes in between initial render and mount,
// `useSyncExternalStore` should handle that by checking the state version and issuing update.
(_a = adm.onStoreChange) === null || _a === void 0 ? void 0 : _a.call(adm);
});
}
export function useObserver(render, baseComponentName) {
if (baseComponentName === void 0) { baseComponentName = "observed"; }
if (isUsingStaticRendering()) {
return render();
}
var admRef = React.useRef(null);
if (!admRef.current) {
// First render
var adm_1 = {
reaction: null,
onStoreChange: null,
stateVersion: Symbol(),
name: baseComponentName,
subscribe: function (onStoreChange) {
// Do NOT access admRef here!
observerFinalizationRegistry.unregister(adm_1);
adm_1.onStoreChange = onStoreChange;
if (!adm_1.reaction) {
// We've lost our reaction and therefore all subscriptions, occurs when:
// 1. Timer based finalization registry disposed reaction before component mounted.
// 2. React "re-mounts" same component without calling render in between (typically <StrictMode>).
// We have to recreate reaction and schedule re-render to recreate subscriptions,
// even if state did not change.
createReaction(adm_1);
// `onStoreChange` won't force update if subsequent `getSnapshot` returns same value.
// So we make sure that is not the case
adm_1.stateVersion = Symbol();
}
return function () {
var _a;
// Do NOT access admRef here!
adm_1.onStoreChange = null;
(_a = adm_1.reaction) === null || _a === void 0 ? void 0 : _a.dispose();
adm_1.reaction = null;
};
},
getSnapshot: function () {
// Do NOT access admRef here!
return adm_1.stateVersion;
}
};
admRef.current = adm_1;
}
var adm = admRef.current;
if (!adm.reaction) {
// First render or reaction was disposed by registry before subscribe
createReaction(adm);
// StrictMode/ConcurrentMode/Suspense may mean that our component is
// rendered and abandoned multiple times, so we need to track leaked
// Reactions.
observerFinalizationRegistry.register(admRef, adm, adm);
}
React.useDebugValue(adm.reaction, printDebugValue);
useSyncExternalStore(
// Both of these must be stable, otherwise it would keep resubscribing every render.
adm.subscribe, adm.getSnapshot, adm.getSnapshot);
// render the original component, but have the
// reaction track the observables, so that rendering
// can be invalidated (see above) once a dependency changes
var renderResult;
var exception;
adm.reaction.track(function () {
try {
renderResult = render();
}
catch (e) {
exception = e;
}
});
if (exception) {
throw exception; // re-throw any exceptions caught during rendering
}
return renderResult;
}
//# sourceMappingURL=useObserver.js.map