react-bindings.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import * as React from 'react';
  2. import React__default, { useState, useEffect, useRef, useMemo, createContext, useCallback, useContext } from 'react';
  3. import { RediError, Injector, Quantity } from '@wendellhu/redi';
  4. import { BehaviorSubject } from 'rxjs';
  5. var __REDI_CONTEXT_LOCK__ = 'REDI_CONTEXT_LOCK';
  6. var isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
  7. var globalObject = (typeof globalThis !== 'undefined' && globalThis) ||
  8. (typeof window !== 'undefined' && window) ||
  9. (typeof global !== 'undefined' && global);
  10. if (!globalObject[__REDI_CONTEXT_LOCK__]) {
  11. globalObject[__REDI_CONTEXT_LOCK__] = true;
  12. }
  13. else if (!isNode) {
  14. console.error('[redi]: "RediContext" is already created. You may import "RediContext" from different paths. Use "import { RediContext } from \'@wendellhu/redi/react-bindings\'; instead."');
  15. }
  16. var RediContext = React.createContext({
  17. injector: null,
  18. });
  19. RediContext.displayName = 'RediContext';
  20. var RediProvider = RediContext.Provider;
  21. var RediConsumer = RediContext.Consumer;
  22. var __extends$1 = (undefined && undefined.__extends) || (function () {
  23. var extendStatics = function (d, b) {
  24. extendStatics = Object.setPrototypeOf ||
  25. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  26. function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
  27. return extendStatics(d, b);
  28. };
  29. return function (d, b) {
  30. if (typeof b !== "function" && b !== null)
  31. throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
  32. extendStatics(d, b);
  33. function __() { this.constructor = d; }
  34. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  35. };
  36. })();
  37. var HooksNotInRediContextError = /** @class */ (function (_super) {
  38. __extends$1(HooksNotInRediContextError, _super);
  39. function HooksNotInRediContextError() {
  40. return _super.call(this, 'Using dependency injection outside of a RediContext.') || this;
  41. }
  42. return HooksNotInRediContextError;
  43. }(RediError));
  44. function useInjector() {
  45. var injectionContext = React.useContext(RediContext);
  46. if (!injectionContext.injector) {
  47. throw new HooksNotInRediContextError();
  48. }
  49. return injectionContext.injector;
  50. }
  51. function useDependency(id, quantityOrLookUp, lookUp) {
  52. var injector = useInjector();
  53. return injector.get(id, quantityOrLookUp, lookUp);
  54. }
  55. var __assign = (undefined && undefined.__assign) || function () {
  56. __assign = Object.assign || function(t) {
  57. for (var s, i = 1, n = arguments.length; i < n; i++) {
  58. s = arguments[i];
  59. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  60. t[p] = s[p];
  61. }
  62. return t;
  63. };
  64. return __assign.apply(this, arguments);
  65. };
  66. function RediInjector(props) {
  67. var children = props.children, dependencies = props.dependencies;
  68. var childInjectorRef = React.useRef(null);
  69. // dispose the injector when the container Injector unmounts
  70. React.useEffect(function () { return function () { var _a; return (_a = childInjectorRef.current) === null || _a === void 0 ? void 0 : _a.dispose(); }; }, []);
  71. return (React.createElement(RediConsumer, null, function (context) {
  72. var childInjector;
  73. if (childInjectorRef.current) {
  74. childInjector = childInjectorRef.current;
  75. }
  76. else {
  77. childInjector = context.injector
  78. ? context.injector.createChild(dependencies)
  79. : new Injector(dependencies);
  80. childInjectorRef.current = childInjector;
  81. }
  82. return (React.createElement(RediProvider, { value: { injector: childInjector } }, children));
  83. }));
  84. }
  85. /**
  86. * @param Comp
  87. * @param injector
  88. * @returns
  89. */
  90. function connectInjector(Comp, injector) {
  91. return function ComponentWithInjector(props) {
  92. return (React.createElement(RediProvider, { value: { injector: injector } },
  93. React.createElement(Comp, __assign({}, props))));
  94. };
  95. }
  96. function connectDependencies(Comp, dependencies) {
  97. return function ComponentWithInjector(props) {
  98. return (React.createElement(RediInjector, { dependencies: dependencies },
  99. React.createElement(Comp, __assign({}, props))));
  100. };
  101. }
  102. var __extends = (undefined && undefined.__extends) || (function () {
  103. var extendStatics = function (d, b) {
  104. extendStatics = Object.setPrototypeOf ||
  105. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  106. function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
  107. return extendStatics(d, b);
  108. };
  109. return function (d, b) {
  110. if (typeof b !== "function" && b !== null)
  111. throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
  112. extendStatics(d, b);
  113. function __() { this.constructor = d; }
  114. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  115. };
  116. })();
  117. var ClassComponentNotInRediContextError = /** @class */ (function (_super) {
  118. __extends(ClassComponentNotInRediContextError, _super);
  119. function ClassComponentNotInRediContextError(component) {
  120. return _super.call(this, "You should make \"RediContext\" as ".concat(component.constructor.name, "'s default context type. ") +
  121. 'If you want to use multiple context, please check this on React doc site. ' +
  122. 'https://reactjs.org/docs/context.html#classcontexttype') || this;
  123. }
  124. return ClassComponentNotInRediContextError;
  125. }(RediError));
  126. function WithDependency(id, quantity, lookUp) {
  127. return function () {
  128. return {
  129. get: function () {
  130. var thisComponent = this;
  131. var context = thisComponent.context;
  132. if (!context || !context.injector) {
  133. throw new ClassComponentNotInRediContextError(thisComponent);
  134. }
  135. var injector = context.injector;
  136. var thing = injector.get(id, quantity || Quantity.REQUIRED, lookUp);
  137. return thing;
  138. },
  139. };
  140. };
  141. }
  142. var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
  143. if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
  144. if (ar || !(i in from)) {
  145. if (!ar) ar = Array.prototype.slice.call(from, 0, i);
  146. ar[i] = from[i];
  147. }
  148. }
  149. return to.concat(ar || Array.prototype.slice.call(from));
  150. };
  151. /**
  152. * unwrap an observable value, return it to the component for rendering, and
  153. * trigger re-render when value changes
  154. *
  155. * **IMPORTANT**. Parent and child components better not subscribe to the same
  156. * observable, otherwise unnecessary re-render would be triggered. Instead, the
  157. * top-most component should subscribe and pass value of the observable to
  158. * its offspring, by props or context. Please consider using `useDependencyContext` and
  159. * `useDependencyContextValue` in this case.
  160. *
  161. * @deprecated Please use `useObservable` instead.
  162. */
  163. function useDependencyValue(depValue$, defaultValue) {
  164. var firstValue = depValue$ instanceof BehaviorSubject && typeof defaultValue === 'undefined'
  165. ? depValue$.getValue()
  166. : defaultValue;
  167. var _a = useState(firstValue), value = _a[0], setValue = _a[1];
  168. useEffect(function () {
  169. var subscription = depValue$.subscribe(function (val) { return setValue(val); });
  170. return function () { return subscription.unsubscribe(); };
  171. }, [depValue$]);
  172. return value;
  173. }
  174. function unwrap(o) {
  175. if (typeof o === 'function') {
  176. return o();
  177. }
  178. return o;
  179. }
  180. /**
  181. * Subscribe to an observable and return its value. The component will re-render when the observable emits a new value.
  182. *
  183. * @param observable An observable or a function that returns an observable
  184. * @param defaultValue The default value of the observable. It the `observable` can omit an initial value, this value will be neglected.
  185. * @param shouldHaveSyncValue If the observable should have a sync value. If it does not have a sync value, an error will be thrown.
  186. * @param deps A dependency array to decide if we should re-subscribe when the `observable` is a function.
  187. * @returns
  188. */
  189. function useObservable(observable, defaultValue, shouldHaveSyncValue, deps) {
  190. if (typeof observable === 'function' && !deps) {
  191. throw new RediError("Expected deps to be provided when observable is a function!");
  192. }
  193. var observableRef = useRef(null);
  194. var initializedRef = useRef(false);
  195. // eslint-disable-next-line react-hooks/exhaustive-deps
  196. var destObservable = useMemo(function () { return observable; }, __spreadArray([], (typeof deps !== 'undefined' ? deps : [observable]), true));
  197. // This state is only for trigger React to re-render. We do not use `setValue` directly because it may cause
  198. // memory leaking.
  199. var _a = useState(0), _ = _a[0], setRenderCounter = _a[1];
  200. var valueRef = useRef((function () {
  201. var innerDefaultValue;
  202. if (destObservable) {
  203. var sub = unwrap(destObservable).subscribe(function (value) {
  204. initializedRef.current = true;
  205. innerDefaultValue = value;
  206. });
  207. sub.unsubscribe();
  208. }
  209. return innerDefaultValue !== null && innerDefaultValue !== void 0 ? innerDefaultValue : defaultValue;
  210. })());
  211. useEffect(function () {
  212. var subscription = null;
  213. if (destObservable) {
  214. observableRef.current = unwrap(destObservable);
  215. subscription = observableRef.current.subscribe(function (value) {
  216. valueRef.current = value;
  217. setRenderCounter(function (prev) { return prev + 1; });
  218. });
  219. }
  220. return function () { return subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe(); };
  221. }, [destObservable]);
  222. if (shouldHaveSyncValue && !initializedRef.current) {
  223. throw new Error('Expect `shouldHaveSyncValue` but not getting a sync value!');
  224. }
  225. return valueRef.current;
  226. }
  227. /**
  228. * subscribe to a signal that emits whenever data updates and re-render
  229. *
  230. * @param update$ a signal that the data the functional component depends has updated
  231. */
  232. function useUpdateBinder(update$) {
  233. var _a = useState(0), dumpSet = _a[1];
  234. useEffect(function () {
  235. var subscription = update$.subscribe(function () { return dumpSet(function (prev) { return prev + 1; }); });
  236. return function () { return subscription.unsubscribe(); };
  237. }, []);
  238. }
  239. var DepValueMapProvider = new WeakMap();
  240. /**
  241. * subscribe to an observable value from a service, creating a context for it so
  242. * it child component won't have to subscribe again and cause unnecessary
  243. */
  244. function useDependencyContext(depValue$, defaultValue) {
  245. var depRef = useRef(undefined);
  246. var value = useDependencyValue(depValue$, defaultValue);
  247. var Context = useMemo(function () {
  248. return createContext(value);
  249. }, [depValue$]);
  250. var Provider = useCallback(function (props) {
  251. return React__default.createElement(Context.Provider, { value: value }, props.children);
  252. }, [depValue$, value]);
  253. if (depRef.current !== depValue$) {
  254. if (depRef.current) {
  255. DepValueMapProvider.delete(depRef.current);
  256. }
  257. depRef.current = depValue$;
  258. DepValueMapProvider.set(depValue$, Context);
  259. }
  260. return {
  261. Provider: Provider,
  262. value: value,
  263. };
  264. }
  265. function useDependencyContextValue(depValue$) {
  266. var context = DepValueMapProvider.get(depValue$);
  267. if (!context) {
  268. throw new RediError("try to read context value but no ancestor component subscribed it.");
  269. }
  270. return useContext(context);
  271. }
  272. export { RediConsumer, RediContext, RediProvider, WithDependency, connectDependencies, connectInjector, useDependency, useDependencyContext, useDependencyContextValue, useDependencyValue, useInjector, useObservable, useUpdateBinder };
  273. //# sourceMappingURL=react-bindings.js.map