custom.module.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. import { spy, observable, computed, getDependencyTree, Reaction, runInAction, transaction, configure } from 'mobx';
  2. import React, { useState, useRef, useMemo, useEffect, useCallback, useDebugValue, memo, forwardRef } from 'react';
  3. if (!useState) {
  4. throw new Error("mobx-react-lite requires React with Hooks support");
  5. }
  6. if (!spy) {
  7. throw new Error("mobx-react-lite requires mobx at least version 4 to be available");
  8. }
  9. function useObservable(initialValue) {
  10. var observableRef = useRef(null);
  11. if (!observableRef.current) {
  12. observableRef.current = observable(initialValue);
  13. }
  14. return observableRef.current;
  15. }
  16. function useComputed(func, inputs) {
  17. if (inputs === void 0) { inputs = []; }
  18. var computed$1 = useMemo(function () { return computed(func); }, inputs);
  19. return computed$1.get();
  20. }
  21. var doNothingDisposer = function () {
  22. // empty
  23. };
  24. /**
  25. * Adds an observable effect (reaction, autorun, or anything else that returns a disposer) that will be registered upon component creation and disposed upon unmounting.
  26. * Returns the generated disposer for early disposal.
  27. *
  28. * @export
  29. * @template D
  30. * @param {() => D} disposerGenerator A function that returns the disposer of the wanted effect.
  31. * @param {ReadonlyArray<any>} [inputs=[]] If you want the effect to be automatically re-created when some variable(s) are changed then pass them in this array.
  32. * @returns {D}
  33. */
  34. function useDisposable(disposerGenerator, inputs) {
  35. if (inputs === void 0) { inputs = []; }
  36. var disposerRef = useRef(null);
  37. var earlyDisposedRef = useRef(false);
  38. useEffect(function () {
  39. return lazyCreateDisposer(false);
  40. }, inputs);
  41. function lazyCreateDisposer(earlyDisposal) {
  42. // ensure that we won't create a new disposer if it was early disposed
  43. if (earlyDisposedRef.current) {
  44. return doNothingDisposer;
  45. }
  46. if (!disposerRef.current) {
  47. var newDisposer = disposerGenerator();
  48. if (typeof newDisposer !== "function") {
  49. var error = new Error("generated disposer must be a function");
  50. {
  51. // tslint:disable-next-line:no-console
  52. console.error(error);
  53. return doNothingDisposer;
  54. }
  55. }
  56. disposerRef.current = newDisposer;
  57. }
  58. return function () {
  59. if (disposerRef.current) {
  60. disposerRef.current();
  61. disposerRef.current = null;
  62. }
  63. if (earlyDisposal) {
  64. earlyDisposedRef.current = true;
  65. }
  66. };
  67. }
  68. return lazyCreateDisposer(true);
  69. }
  70. var globalIsUsingStaticRendering = false;
  71. function useStaticRendering(enable) {
  72. globalIsUsingStaticRendering = enable;
  73. }
  74. function isUsingStaticRendering() {
  75. return globalIsUsingStaticRendering;
  76. }
  77. /*! *****************************************************************************
  78. Copyright (c) Microsoft Corporation. All rights reserved.
  79. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  80. this file except in compliance with the License. You may obtain a copy of the
  81. License at http://www.apache.org/licenses/LICENSE-2.0
  82. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  83. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  84. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  85. MERCHANTABLITY OR NON-INFRINGEMENT.
  86. See the Apache Version 2.0 License for specific language governing permissions
  87. and limitations under the License.
  88. ***************************************************************************** */
  89. var __assign = function() {
  90. __assign = Object.assign || function __assign(t) {
  91. for (var s, i = 1, n = arguments.length; i < n; i++) {
  92. s = arguments[i];
  93. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  94. }
  95. return t;
  96. };
  97. return __assign.apply(this, arguments);
  98. };
  99. function __read(o, n) {
  100. var m = typeof Symbol === "function" && o[Symbol.iterator];
  101. if (!m) return o;
  102. var i = m.call(o), r, ar = [], e;
  103. try {
  104. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  105. }
  106. catch (error) { e = { error: error }; }
  107. finally {
  108. try {
  109. if (r && !r.done && (m = i["return"])) m.call(i);
  110. }
  111. finally { if (e) throw e.error; }
  112. }
  113. return ar;
  114. }
  115. function printDebugValue(v) {
  116. if (!v.current) {
  117. return "<unknown>";
  118. }
  119. return getDependencyTree(v.current);
  120. }
  121. var EMPTY_ARRAY = [];
  122. function useUnmount(fn) {
  123. useEffect(function () { return fn; }, EMPTY_ARRAY);
  124. }
  125. function useForceUpdate() {
  126. var _a = __read(useState(0), 2), setTick = _a[1];
  127. var update = useCallback(function () {
  128. setTick(function (tick) { return tick + 1; });
  129. }, []);
  130. return update;
  131. }
  132. function isPlainObject(value) {
  133. if (!value || typeof value !== "object") {
  134. return false;
  135. }
  136. var proto = Object.getPrototypeOf(value);
  137. return !proto || proto === Object.prototype;
  138. }
  139. var EMPTY_OBJECT = {};
  140. function useObserver(fn, baseComponentName, options) {
  141. if (baseComponentName === void 0) { baseComponentName = "observed"; }
  142. if (options === void 0) { options = EMPTY_OBJECT; }
  143. if (isUsingStaticRendering()) {
  144. return fn();
  145. }
  146. var wantedForceUpdateHook = options.useForceUpdate || useForceUpdate;
  147. var forceUpdate = wantedForceUpdateHook();
  148. var reaction = useRef(null);
  149. if (!reaction.current) {
  150. reaction.current = new Reaction("observer(" + baseComponentName + ")", function () {
  151. forceUpdate();
  152. });
  153. }
  154. var dispose = function () {
  155. if (reaction.current && !reaction.current.isDisposed) {
  156. reaction.current.dispose();
  157. reaction.current = null;
  158. }
  159. };
  160. useDebugValue(reaction, printDebugValue);
  161. useUnmount(function () {
  162. dispose();
  163. });
  164. // render the original component, but have the
  165. // reaction track the observables, so that rendering
  166. // can be invalidated (see above) once a dependency changes
  167. var rendering;
  168. var exception;
  169. reaction.current.track(function () {
  170. try {
  171. rendering = fn();
  172. }
  173. catch (e) {
  174. exception = e;
  175. }
  176. });
  177. if (exception) {
  178. dispose();
  179. throw exception; // re-throw any exceptions catched during rendering
  180. }
  181. return rendering;
  182. }
  183. // n.b. base case is not used for actual typings or exported in the typing files
  184. function observer(baseComponent, options) {
  185. // The working of observer is explained step by step in this talk: https://www.youtube.com/watch?v=cPF4iBedoF0&feature=youtu.be&t=1307
  186. if (isUsingStaticRendering()) {
  187. return baseComponent;
  188. }
  189. var realOptions = __assign({ forwardRef: false }, options);
  190. var baseComponentName = baseComponent.displayName || baseComponent.name;
  191. var wrappedComponent = function (props, ref) {
  192. return useObserver(function () { return baseComponent(props, ref); }, baseComponentName);
  193. };
  194. wrappedComponent.displayName = baseComponentName;
  195. // memo; we are not intested in deep updates
  196. // in props; we assume that if deep objects are changed,
  197. // this is in observables, which would have been tracked anyway
  198. var memoComponent;
  199. if (realOptions.forwardRef) {
  200. // we have to use forwardRef here because:
  201. // 1. it cannot go before memo, only after it
  202. // 2. forwardRef converts the function into an actual component, so we can't let the baseComponent do it
  203. // since it wouldn't be a callable function anymore
  204. memoComponent = memo(forwardRef(wrappedComponent));
  205. }
  206. else {
  207. memoComponent = memo(wrappedComponent);
  208. }
  209. copyStaticProperties(baseComponent, memoComponent);
  210. memoComponent.displayName = baseComponentName;
  211. return memoComponent;
  212. }
  213. // based on https://github.com/mridgway/hoist-non-react-statics/blob/master/src/index.js
  214. var hoistBlackList = {
  215. $$typeof: true,
  216. render: true,
  217. compare: true,
  218. type: true
  219. };
  220. function copyStaticProperties(base, target) {
  221. Object.keys(base).forEach(function (key) {
  222. if (base.hasOwnProperty(key) && !hoistBlackList[key]) {
  223. Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(base, key));
  224. }
  225. });
  226. }
  227. function ObserverComponent(_a) {
  228. var children = _a.children, render = _a.render;
  229. var component = children || render;
  230. if (typeof component !== "function") {
  231. return null;
  232. }
  233. return useObserver(component);
  234. }
  235. ObserverComponent.propTypes = {
  236. children: ObserverPropsCheck,
  237. render: ObserverPropsCheck
  238. };
  239. ObserverComponent.displayName = "Observer";
  240. function ObserverPropsCheck(props, key, componentName, location, propFullName) {
  241. var extraKey = key === "children" ? "render" : "children";
  242. var hasProp = typeof props[key] === "function";
  243. var hasExtraProp = typeof props[extraKey] === "function";
  244. if (hasProp && hasExtraProp) {
  245. return new Error("MobX Observer: Do not use children and render in the same time in`" + componentName);
  246. }
  247. if (hasProp || hasExtraProp) {
  248. return null;
  249. }
  250. return new Error("Invalid prop `" +
  251. propFullName +
  252. "` of type `" +
  253. typeof props[key] +
  254. "` supplied to" +
  255. " `" +
  256. componentName +
  257. "`, expected `function`.");
  258. }
  259. function useAsObservableSourceInternal(current, usedByLocalStore) {
  260. if (usedByLocalStore && current === undefined) {
  261. return undefined;
  262. }
  263. var _a = __read(React.useState(function () { return observable(current, {}, { deep: false }); }), 1), res = _a[0];
  264. runInAction(function () {
  265. Object.assign(res, current);
  266. });
  267. return res;
  268. }
  269. function useAsObservableSource(current) {
  270. return useAsObservableSourceInternal(current, false);
  271. }
  272. function useLocalStore(initializer, current) {
  273. var source = useAsObservableSourceInternal(current, true);
  274. return React.useState(function () {
  275. var local = observable(initializer(source));
  276. if (isPlainObject(local)) {
  277. runInAction(function () {
  278. Object.keys(local).forEach(function (key) {
  279. var value = local[key];
  280. if (typeof value === "function") {
  281. // @ts-ignore No idea why ts2536 is popping out here
  282. local[key] = wrapInTransaction(value, local);
  283. }
  284. });
  285. });
  286. }
  287. return local;
  288. })[0];
  289. }
  290. // tslint:disable-next-line: ban-types
  291. function wrapInTransaction(fn, context) {
  292. return function () {
  293. var args = [];
  294. for (var _i = 0; _i < arguments.length; _i++) {
  295. args[_i] = arguments[_i];
  296. }
  297. return transaction(function () { return fn.apply(context, args); });
  298. };
  299. }
  300. var optimizeScheduler = function (reactionScheduler) {
  301. if (typeof reactionScheduler === "function") {
  302. configure({ reactionScheduler: reactionScheduler });
  303. }
  304. };
  305. export { ObserverComponent as Observer, isUsingStaticRendering, observer, optimizeScheduler, useAsObservableSource, useComputed, useDisposable, useForceUpdate, useLocalStore, useObservable, useObserver, useStaticRendering };