UNPKG

3.97 kBJavaScriptView Raw
1import { Reaction } from "mobx";
2import React from "react";
3import { printDebugValue } from "./utils/printDebugValue";
4import { isUsingStaticRendering } from "./staticRendering";
5import { observerFinalizationRegistry } from "./utils/observerFinalizationRegistry";
6import { useSyncExternalStore } from "use-sync-external-store/shim";
7function createReaction(adm) {
8 adm.reaction = new Reaction("observer".concat(adm.name), function () {
9 var _a;
10 adm.stateVersion = Symbol();
11 // onStoreChange won't be available until the component "mounts".
12 // If state changes in between initial render and mount,
13 // `useSyncExternalStore` should handle that by checking the state version and issuing update.
14 (_a = adm.onStoreChange) === null || _a === void 0 ? void 0 : _a.call(adm);
15 });
16}
17export function useObserver(render, baseComponentName) {
18 if (baseComponentName === void 0) { baseComponentName = "observed"; }
19 if (isUsingStaticRendering()) {
20 return render();
21 }
22 var admRef = React.useRef(null);
23 if (!admRef.current) {
24 // First render
25 var adm_1 = {
26 reaction: null,
27 onStoreChange: null,
28 stateVersion: Symbol(),
29 name: baseComponentName,
30 subscribe: function (onStoreChange) {
31 // Do NOT access admRef here!
32 observerFinalizationRegistry.unregister(adm_1);
33 adm_1.onStoreChange = onStoreChange;
34 if (!adm_1.reaction) {
35 // We've lost our reaction and therefore all subscriptions, occurs when:
36 // 1. Timer based finalization registry disposed reaction before component mounted.
37 // 2. React "re-mounts" same component without calling render in between (typically <StrictMode>).
38 // We have to recreate reaction and schedule re-render to recreate subscriptions,
39 // even if state did not change.
40 createReaction(adm_1);
41 // `onStoreChange` won't force update if subsequent `getSnapshot` returns same value.
42 // So we make sure that is not the case
43 adm_1.stateVersion = Symbol();
44 }
45 return function () {
46 var _a;
47 // Do NOT access admRef here!
48 adm_1.onStoreChange = null;
49 (_a = adm_1.reaction) === null || _a === void 0 ? void 0 : _a.dispose();
50 adm_1.reaction = null;
51 };
52 },
53 getSnapshot: function () {
54 // Do NOT access admRef here!
55 return adm_1.stateVersion;
56 }
57 };
58 admRef.current = adm_1;
59 }
60 var adm = admRef.current;
61 if (!adm.reaction) {
62 // First render or reaction was disposed by registry before subscribe
63 createReaction(adm);
64 // StrictMode/ConcurrentMode/Suspense may mean that our component is
65 // rendered and abandoned multiple times, so we need to track leaked
66 // Reactions.
67 observerFinalizationRegistry.register(admRef, adm, adm);
68 }
69 React.useDebugValue(adm.reaction, printDebugValue);
70 useSyncExternalStore(
71 // Both of these must be stable, otherwise it would keep resubscribing every render.
72 adm.subscribe, adm.getSnapshot, adm.getSnapshot);
73 // render the original component, but have the
74 // reaction track the observables, so that rendering
75 // can be invalidated (see above) once a dependency changes
76 var renderResult;
77 var exception;
78 adm.reaction.track(function () {
79 try {
80 renderResult = render();
81 }
82 catch (e) {
83 exception = e;
84 }
85 });
86 if (exception) {
87 throw exception; // re-throw any exceptions caught during rendering
88 }
89 return renderResult;
90}
91//# sourceMappingURL=useObserver.js.map
\No newline at end of file