react-rx 3.0.0-4 → 3.0.0-5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/index.d.mts +17 -0
  2. package/dist/index.d.ts +17 -0
  3. package/dist/index.js +52 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +54 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +19 -14
  8. package/src/useObservable.ts +3 -1
  9. package/src/useObservableEvent.ts +4 -29
  10. package/dist/cjs/__tests__/strictmode.test.d.ts +0 -1
  11. package/dist/cjs/__tests__/strictmode.test.js +0 -96
  12. package/dist/cjs/__tests__/useObservable.test.d.ts +0 -1
  13. package/dist/cjs/__tests__/useObservable.test.js +0 -116
  14. package/dist/cjs/index.d.ts +0 -2
  15. package/dist/cjs/index.js +0 -18
  16. package/dist/cjs/useObservable.d.ts +0 -4
  17. package/dist/cjs/useObservable.js +0 -48
  18. package/dist/cjs/useObservableEvent.d.ts +0 -8
  19. package/dist/cjs/useObservableEvent.js +0 -52
  20. package/dist/es2015/__tests__/strictmode.test.d.ts +0 -1
  21. package/dist/es2015/__tests__/strictmode.test.js +0 -58
  22. package/dist/es2015/__tests__/useObservable.test.d.ts +0 -1
  23. package/dist/es2015/__tests__/useObservable.test.js +0 -110
  24. package/dist/es2015/index.d.ts +0 -2
  25. package/dist/es2015/index.js +0 -2
  26. package/dist/es2015/useObservable.d.ts +0 -4
  27. package/dist/es2015/useObservable.js +0 -44
  28. package/dist/es2015/useObservableEvent.d.ts +0 -8
  29. package/dist/es2015/useObservableEvent.js +0 -41
  30. package/dist/esm/__tests__/strictmode.test.d.ts +0 -1
  31. package/dist/esm/__tests__/strictmode.test.js +0 -94
  32. package/dist/esm/__tests__/useObservable.test.d.ts +0 -1
  33. package/dist/esm/__tests__/useObservable.test.js +0 -114
  34. package/dist/esm/index.d.ts +0 -2
  35. package/dist/esm/index.js +0 -2
  36. package/dist/esm/useObservable.d.ts +0 -4
  37. package/dist/esm/useObservable.js +0 -44
  38. package/dist/esm/useObservableEvent.d.ts +0 -8
  39. package/dist/esm/useObservableEvent.js +0 -46
@@ -0,0 +1,17 @@
1
+ import {Observable} from 'rxjs'
2
+
3
+ /** @internal */
4
+ export declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never
5
+
6
+ /** @public */
7
+ export declare function useObservable<ObservableType extends Observable<any>>(
8
+ observable: ObservableType,
9
+ initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
10
+ ): UnboxObservable<ObservableType>
11
+
12
+ /** @public */
13
+ export declare function useObservableEvent<T, U>(
14
+ fn: (arg: Observable<T>) => Observable<U>,
15
+ ): (arg: T) => void
16
+
17
+ export {}
@@ -0,0 +1,17 @@
1
+ import {Observable} from 'rxjs'
2
+
3
+ /** @internal */
4
+ export declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never
5
+
6
+ /** @public */
7
+ export declare function useObservable<ObservableType extends Observable<any>>(
8
+ observable: ObservableType,
9
+ initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
10
+ ): UnboxObservable<ObservableType>
11
+
12
+ /** @public */
13
+ export declare function useObservableEvent<T, U>(
14
+ fn: (arg: Observable<T>) => Observable<U>,
15
+ ): (arg: T) => void
16
+
17
+ export {}
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var react = require("react"), operators = require("rxjs/operators"), observableCallback = require("observable-callback");
4
+ function getValue(value) {
5
+ return typeof value == "function" ? value() : value;
6
+ }
7
+ const cache = /* @__PURE__ */ new WeakMap();
8
+ function useObservable(observable, initialValue) {
9
+ const initialValueRef = react.useRef(getValue(initialValue));
10
+ react.useEffect(() => {
11
+ initialValueRef.current = getValue(initialValue);
12
+ }, [initialValue]);
13
+ const store = react.useMemo(() => {
14
+ if (!cache.has(observable)) {
15
+ const entry = {
16
+ snapshot: initialValueRef.current
17
+ };
18
+ entry.observable = observable.pipe(
19
+ operators.shareReplay({ refCount: !0, bufferSize: 1 }),
20
+ operators.tap((value) => entry.snapshot = value)
21
+ ), entry.subscription = entry.observable.subscribe(), cache.set(observable, entry);
22
+ }
23
+ const instance = cache.get(observable);
24
+ return instance.subscription.closed && (instance.subscription = instance.observable.subscribe()), {
25
+ subscribe: (onStoreChange) => {
26
+ const subscription = instance.observable.subscribe(onStoreChange);
27
+ return instance.subscription.unsubscribe(), () => subscription.unsubscribe();
28
+ },
29
+ getSnapshot: () => instance.snapshot
30
+ };
31
+ }, [observable]);
32
+ return react.useSyncExternalStore(store.subscribe, store.getSnapshot);
33
+ }
34
+ function useObservableEvent(fn) {
35
+ const [[calls$, call]] = react.useState(() => observableCallback.observableCallback()), callback = useEffectEvent(fn);
36
+ return react.useEffect(() => {
37
+ const subscription = calls$.pipe(callback).subscribe();
38
+ return () => subscription.unsubscribe();
39
+ }, [callback, calls$]), call;
40
+ }
41
+ function useEffectEvent(fn) {
42
+ const ref = react.useRef(null);
43
+ return react.useInsertionEffect(() => {
44
+ ref.current = fn;
45
+ }, [fn]), react.useCallback((...args) => {
46
+ const f = ref.current;
47
+ return f(...args);
48
+ }, []);
49
+ }
50
+ exports.useObservable = useObservable;
51
+ exports.useObservableEvent = useObservableEvent;
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/useObservable.ts","../src/useObservableEvent.ts"],"sourcesContent":["import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'\nimport type {Observable, Subscription} from 'rxjs'\nimport {shareReplay, tap} from 'rxjs/operators'\n\nfunction getValue<T>(value: T): T extends () => infer U ? U : T {\n return typeof value === 'function' ? value() : value\n}\ninterface CacheRecord<T> {\n subscription: Subscription\n observable: Observable<T>\n snapshot: T\n}\n\nconst cache = new WeakMap<Observable<any>, CacheRecord<any>>()\n\n/** @public */\nexport function useObservable<ObservableType extends Observable<any>>(\n observable: ObservableType,\n initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),\n): UnboxObservable<ObservableType> {\n /**\n * Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.\n * But we also don't want the initialValue to be stale if the observable changes.\n */\n const initialValueRef = useRef(getValue(initialValue) as UnboxObservable<ObservableType>)\n\n /**\n * Ensures that the initialValue is always up-to-date in case the observable changes.\n */\n useEffect(() => {\n initialValueRef.current = getValue(initialValue) as UnboxObservable<ObservableType>\n }, [initialValue])\n\n const store = useMemo(() => {\n if (!cache.has(observable)) {\n const entry: Partial<CacheRecord<UnboxObservable<ObservableType>>> = {\n snapshot: initialValueRef.current,\n }\n entry.observable = observable.pipe(\n shareReplay({refCount: true, bufferSize: 1}),\n tap((value) => (entry.snapshot = value)),\n )\n\n // Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.\n entry.subscription = entry.observable.subscribe()\n\n cache.set(observable, entry as CacheRecord<UnboxObservable<ObservableType>>)\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const instance = cache.get(observable)!\n if (instance.subscription.closed) {\n instance.subscription = instance.observable.subscribe()\n }\n\n return {\n subscribe: (onStoreChange: () => void) => {\n const subscription = instance.observable.subscribe(onStoreChange)\n instance.subscription.unsubscribe()\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => instance.snapshot,\n }\n }, [observable])\n\n return useSyncExternalStore<UnboxObservable<ObservableType>>(store.subscribe, store.getSnapshot)\n}\n\n/** @internal */\nexport type UnboxObservable<T> = T extends Observable<infer U> ? U : never\n","import {observableCallback} from 'observable-callback'\nimport {useCallback, useEffect, useInsertionEffect, useRef, useState} from 'react'\nimport {type Observable} from 'rxjs'\n\n/** @public */\nexport function useObservableEvent<T, U>(\n fn: (arg: Observable<T>) => Observable<U>,\n): (arg: T) => void {\n const [[calls$, call]] = useState(() => observableCallback<T>())\n\n const callback = useEffectEvent(fn)\n\n useEffect(() => {\n const subscription = calls$.pipe(callback).subscribe()\n return () => subscription.unsubscribe()\n }, [callback, calls$])\n\n return call\n}\n\n/**\n * This is a ponyfill of the upcoming `useEffectEvent` hook that'll arrive in React 19\n */\nfunction useEffectEvent<\n const T extends (\n ...args: // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any[]\n ) => void,\n>(fn: T): T {\n const ref = useRef<T | null>(null)\n useInsertionEffect(() => {\n ref.current = fn\n }, [fn])\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return useCallback((...args: any[]) => {\n const f = ref.current!\n return f(...args)\n }, []) as T\n}\n"],"names":["useRef","useEffect","useMemo","shareReplay","tap","useSyncExternalStore","useState","observableCallback","useInsertionEffect","useCallback"],"mappings":";;;AAIA,SAAS,SAAY,OAA2C;AAC9D,SAAO,OAAO,SAAU,aAAa,MAAA,IAAU;AACjD;AAOA,MAAM,4BAAY;AAGF,SAAA,cACd,YACA,cACiC;AAKjC,QAAM,kBAAkBA,MAAA,OAAO,SAAS,YAAY,CAAoC;AAKxFC,QAAAA,UAAU,MAAM;AACE,oBAAA,UAAU,SAAS,YAAY;AAAA,EAAA,GAC9C,CAAC,YAAY,CAAC;AAEX,QAAA,QAAQC,MAAAA,QAAQ,MAAM;AAC1B,QAAI,CAAC,MAAM,IAAI,UAAU,GAAG;AAC1B,YAAM,QAA+D;AAAA,QACnE,UAAU,gBAAgB;AAAA,MAAA;AAE5B,YAAM,aAAa,WAAW;AAAA,QAC5BC,sBAAY,EAAC,UAAU,IAAM,YAAY,GAAE;AAAA,QAC3CC,UAAAA,IAAI,CAAC,UAAW,MAAM,WAAW,KAAM;AAAA,MACzC,GAGA,MAAM,eAAe,MAAM,WAAW,aAEtC,MAAM,IAAI,YAAY,KAAqD;AAAA,IAC7E;AAEM,UAAA,WAAW,MAAM,IAAI,UAAU;AACjC,WAAA,SAAS,aAAa,WACxB,SAAS,eAAe,SAAS,WAAW,cAGvC;AAAA,MACL,WAAW,CAAC,kBAA8B;AACxC,cAAM,eAAe,SAAS,WAAW,UAAU,aAAa;AAChE,eAAA,SAAS,aAAa,YACf,GAAA,MAAM,aAAa;MAC5B;AAAA,MACA,aAAa,MAAM,SAAS;AAAA,IAAA;AAAA,EAC9B,GACC,CAAC,UAAU,CAAC;AAEf,SAAOC,MAAsD,qBAAA,MAAM,WAAW,MAAM,WAAW;AACjG;AC5DO,SAAS,mBACd,IACkB;AAClB,QAAM,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAIC,MAAS,SAAA,MAAMC,mBAAsB,mBAAA,CAAC,GAEzD,WAAW,eAAe,EAAE;AAElC,SAAAN,MAAA,UAAU,MAAM;AACd,UAAM,eAAe,OAAO,KAAK,QAAQ,EAAE,UAAU;AAC9C,WAAA,MAAM,aAAa;EACzB,GAAA,CAAC,UAAU,MAAM,CAAC,GAEd;AACT;AAKA,SAAS,eAKP,IAAU;AACJ,QAAA,MAAMD,aAAiB,IAAI;AACjC,SAAAQ,MAAA,mBAAmB,MAAM;AACvB,QAAI,UAAU;AAAA,KACb,CAAC,EAAE,CAAC,GAEAC,MAAAA,YAAY,IAAI,SAAgB;AACrC,UAAM,IAAI,IAAI;AACP,WAAA,EAAE,GAAG,IAAI;AAAA,EAClB,GAAG,CAAE,CAAA;AACP;;;"}
package/dist/index.mjs ADDED
@@ -0,0 +1,54 @@
1
+ import { useRef, useEffect, useMemo, useSyncExternalStore, useState, useInsertionEffect, useCallback } from "react";
2
+ import { shareReplay, tap } from "rxjs/operators";
3
+ import { observableCallback } from "observable-callback";
4
+ function getValue(value) {
5
+ return typeof value == "function" ? value() : value;
6
+ }
7
+ const cache = /* @__PURE__ */ new WeakMap();
8
+ function useObservable(observable, initialValue) {
9
+ const initialValueRef = useRef(getValue(initialValue));
10
+ useEffect(() => {
11
+ initialValueRef.current = getValue(initialValue);
12
+ }, [initialValue]);
13
+ const store = useMemo(() => {
14
+ if (!cache.has(observable)) {
15
+ const entry = {
16
+ snapshot: initialValueRef.current
17
+ };
18
+ entry.observable = observable.pipe(
19
+ shareReplay({ refCount: !0, bufferSize: 1 }),
20
+ tap((value) => entry.snapshot = value)
21
+ ), entry.subscription = entry.observable.subscribe(), cache.set(observable, entry);
22
+ }
23
+ const instance = cache.get(observable);
24
+ return instance.subscription.closed && (instance.subscription = instance.observable.subscribe()), {
25
+ subscribe: (onStoreChange) => {
26
+ const subscription = instance.observable.subscribe(onStoreChange);
27
+ return instance.subscription.unsubscribe(), () => subscription.unsubscribe();
28
+ },
29
+ getSnapshot: () => instance.snapshot
30
+ };
31
+ }, [observable]);
32
+ return useSyncExternalStore(store.subscribe, store.getSnapshot);
33
+ }
34
+ function useObservableEvent(fn) {
35
+ const [[calls$, call]] = useState(() => observableCallback()), callback = useEffectEvent(fn);
36
+ return useEffect(() => {
37
+ const subscription = calls$.pipe(callback).subscribe();
38
+ return () => subscription.unsubscribe();
39
+ }, [callback, calls$]), call;
40
+ }
41
+ function useEffectEvent(fn) {
42
+ const ref = useRef(null);
43
+ return useInsertionEffect(() => {
44
+ ref.current = fn;
45
+ }, [fn]), useCallback((...args) => {
46
+ const f = ref.current;
47
+ return f(...args);
48
+ }, []);
49
+ }
50
+ export {
51
+ useObservable,
52
+ useObservableEvent
53
+ };
54
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/useObservable.ts","../src/useObservableEvent.ts"],"sourcesContent":["import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'\nimport type {Observable, Subscription} from 'rxjs'\nimport {shareReplay, tap} from 'rxjs/operators'\n\nfunction getValue<T>(value: T): T extends () => infer U ? U : T {\n return typeof value === 'function' ? value() : value\n}\ninterface CacheRecord<T> {\n subscription: Subscription\n observable: Observable<T>\n snapshot: T\n}\n\nconst cache = new WeakMap<Observable<any>, CacheRecord<any>>()\n\n/** @public */\nexport function useObservable<ObservableType extends Observable<any>>(\n observable: ObservableType,\n initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),\n): UnboxObservable<ObservableType> {\n /**\n * Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.\n * But we also don't want the initialValue to be stale if the observable changes.\n */\n const initialValueRef = useRef(getValue(initialValue) as UnboxObservable<ObservableType>)\n\n /**\n * Ensures that the initialValue is always up-to-date in case the observable changes.\n */\n useEffect(() => {\n initialValueRef.current = getValue(initialValue) as UnboxObservable<ObservableType>\n }, [initialValue])\n\n const store = useMemo(() => {\n if (!cache.has(observable)) {\n const entry: Partial<CacheRecord<UnboxObservable<ObservableType>>> = {\n snapshot: initialValueRef.current,\n }\n entry.observable = observable.pipe(\n shareReplay({refCount: true, bufferSize: 1}),\n tap((value) => (entry.snapshot = value)),\n )\n\n // Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.\n entry.subscription = entry.observable.subscribe()\n\n cache.set(observable, entry as CacheRecord<UnboxObservable<ObservableType>>)\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const instance = cache.get(observable)!\n if (instance.subscription.closed) {\n instance.subscription = instance.observable.subscribe()\n }\n\n return {\n subscribe: (onStoreChange: () => void) => {\n const subscription = instance.observable.subscribe(onStoreChange)\n instance.subscription.unsubscribe()\n return () => subscription.unsubscribe()\n },\n getSnapshot: () => instance.snapshot,\n }\n }, [observable])\n\n return useSyncExternalStore<UnboxObservable<ObservableType>>(store.subscribe, store.getSnapshot)\n}\n\n/** @internal */\nexport type UnboxObservable<T> = T extends Observable<infer U> ? U : never\n","import {observableCallback} from 'observable-callback'\nimport {useCallback, useEffect, useInsertionEffect, useRef, useState} from 'react'\nimport {type Observable} from 'rxjs'\n\n/** @public */\nexport function useObservableEvent<T, U>(\n fn: (arg: Observable<T>) => Observable<U>,\n): (arg: T) => void {\n const [[calls$, call]] = useState(() => observableCallback<T>())\n\n const callback = useEffectEvent(fn)\n\n useEffect(() => {\n const subscription = calls$.pipe(callback).subscribe()\n return () => subscription.unsubscribe()\n }, [callback, calls$])\n\n return call\n}\n\n/**\n * This is a ponyfill of the upcoming `useEffectEvent` hook that'll arrive in React 19\n */\nfunction useEffectEvent<\n const T extends (\n ...args: // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any[]\n ) => void,\n>(fn: T): T {\n const ref = useRef<T | null>(null)\n useInsertionEffect(() => {\n ref.current = fn\n }, [fn])\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return useCallback((...args: any[]) => {\n const f = ref.current!\n return f(...args)\n }, []) as T\n}\n"],"names":[],"mappings":";;;AAIA,SAAS,SAAY,OAA2C;AAC9D,SAAO,OAAO,SAAU,aAAa,MAAA,IAAU;AACjD;AAOA,MAAM,4BAAY;AAGF,SAAA,cACd,YACA,cACiC;AAKjC,QAAM,kBAAkB,OAAO,SAAS,YAAY,CAAoC;AAKxF,YAAU,MAAM;AACE,oBAAA,UAAU,SAAS,YAAY;AAAA,EAAA,GAC9C,CAAC,YAAY,CAAC;AAEX,QAAA,QAAQ,QAAQ,MAAM;AAC1B,QAAI,CAAC,MAAM,IAAI,UAAU,GAAG;AAC1B,YAAM,QAA+D;AAAA,QACnE,UAAU,gBAAgB;AAAA,MAAA;AAE5B,YAAM,aAAa,WAAW;AAAA,QAC5B,YAAY,EAAC,UAAU,IAAM,YAAY,GAAE;AAAA,QAC3C,IAAI,CAAC,UAAW,MAAM,WAAW,KAAM;AAAA,MACzC,GAGA,MAAM,eAAe,MAAM,WAAW,aAEtC,MAAM,IAAI,YAAY,KAAqD;AAAA,IAC7E;AAEM,UAAA,WAAW,MAAM,IAAI,UAAU;AACjC,WAAA,SAAS,aAAa,WACxB,SAAS,eAAe,SAAS,WAAW,cAGvC;AAAA,MACL,WAAW,CAAC,kBAA8B;AACxC,cAAM,eAAe,SAAS,WAAW,UAAU,aAAa;AAChE,eAAA,SAAS,aAAa,YACf,GAAA,MAAM,aAAa;MAC5B;AAAA,MACA,aAAa,MAAM,SAAS;AAAA,IAAA;AAAA,EAC9B,GACC,CAAC,UAAU,CAAC;AAEf,SAAO,qBAAsD,MAAM,WAAW,MAAM,WAAW;AACjG;AC5DO,SAAS,mBACd,IACkB;AAClB,QAAM,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,SAAS,MAAM,mBAAsB,CAAC,GAEzD,WAAW,eAAe,EAAE;AAElC,SAAA,UAAU,MAAM;AACd,UAAM,eAAe,OAAO,KAAK,QAAQ,EAAE,UAAU;AAC9C,WAAA,MAAM,aAAa;EACzB,GAAA,CAAC,UAAU,MAAM,CAAC,GAEd;AACT;AAKA,SAAS,eAKP,IAAU;AACJ,QAAA,MAAM,OAAiB,IAAI;AACjC,SAAA,mBAAmB,MAAM;AACvB,QAAI,UAAU;AAAA,KACb,CAAC,EAAE,CAAC,GAEA,YAAY,IAAI,SAAgB;AACrC,UAAM,IAAI,IAAI;AACP,WAAA,EAAE,GAAG,IAAI;AAAA,EAClB,GAAG,CAAE,CAAA;AACP;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-rx",
3
- "version": "3.0.0-4",
3
+ "version": "3.0.0-5",
4
4
  "description": "React + RxJS = <3",
5
5
  "keywords": [
6
6
  "action",
@@ -46,9 +46,18 @@
46
46
  "Cody Olsen <stipsan@gmail.com> (https://github.com/stipsan)"
47
47
  ],
48
48
  "sideEffects": false,
49
- "main": "dist/cjs/index.js",
50
- "module": "dist/esm/index.js",
51
- "types": "dist/cjs/index.d.ts",
49
+ "type": "commonjs",
50
+ "exports": {
51
+ ".": {
52
+ "source": "./src/index.ts",
53
+ "import": "./dist/index.mjs",
54
+ "default": "./dist/index.js"
55
+ },
56
+ "./package.json": "./package.json"
57
+ },
58
+ "main": "./dist/index.js",
59
+ "module": "./dist/index.mjs",
60
+ "types": "./dist/index.d.ts",
52
61
  "files": [
53
62
  "dist",
54
63
  "src"
@@ -58,22 +67,20 @@
58
67
  "."
59
68
  ],
60
69
  "scripts": {
61
- "build": "npm run build:es2015 && npm run build:esm && npm run build:cjs",
62
- "build:es2015": "tsc --module es2015 --target es2015 --outDir dist/es2015",
63
- "build:esm": "tsc --module es2015 --target es5 --outDir dist/esm",
64
- "build:cjs": "tsc --module commonjs --target es5 --outDir dist/cjs",
65
- "clean": "rimraf dist",
70
+ "build": "pkg build --strict --clean --check",
66
71
  "dev": "cd website && npm run dev",
67
- "prepublishOnly": "npm run clean && npm run build",
68
- "watch": "run-p \"build:* -- --watch\"",
72
+ "prepublishOnly": "npm run build",
73
+ "watch": "npm run build -- --watch",
69
74
  "test": "jest",
70
75
  "lint": "eslint --cache ."
71
76
  },
77
+ "browserslist": "extends @sanity/browserslist-config",
72
78
  "prettier": "@sanity/prettier-config",
73
79
  "dependencies": {
74
80
  "observable-callback": "^1.0.3"
75
81
  },
76
82
  "devDependencies": {
83
+ "@sanity/pkg-utils": "^6.9.2",
77
84
  "@sanity/prettier-config": "^1.0.2",
78
85
  "@sanity/semantic-release-preset": "^2.0.5",
79
86
  "@testing-library/dom": "^10.1.0",
@@ -99,7 +106,6 @@
99
106
  "react": "^18.3.1",
100
107
  "react-dom": "^18.3.1",
101
108
  "react-test-renderer": "^18.3.1",
102
- "rimraf": "^3.0.2",
103
109
  "rxjs": "^7.8.1",
104
110
  "ts-jest": "^29.1.1",
105
111
  "typescript": "5.4.5"
@@ -107,6 +113,5 @@
107
113
  "peerDependencies": {
108
114
  "react": "^18.3 || >=19.0.0-rc",
109
115
  "rxjs": "^7"
110
- },
111
- "es2015": "dist/es2015/index.js"
116
+ }
112
117
  }
@@ -13,6 +13,7 @@ interface CacheRecord<T> {
13
13
 
14
14
  const cache = new WeakMap<Observable<any>, CacheRecord<any>>()
15
15
 
16
+ /** @public */
16
17
  export function useObservable<ObservableType extends Observable<any>>(
17
18
  observable: ObservableType,
18
19
  initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
@@ -64,4 +65,5 @@ export function useObservable<ObservableType extends Observable<any>>(
64
65
  return useSyncExternalStore<UnboxObservable<ObservableType>>(store.subscribe, store.getSnapshot)
65
66
  }
66
67
 
67
- type UnboxObservable<T> = T extends Observable<infer U> ? U : never
68
+ /** @internal */
69
+ export type UnboxObservable<T> = T extends Observable<infer U> ? U : never
@@ -1,33 +1,8 @@
1
1
  import {observableCallback} from 'observable-callback'
2
- import {DependencyList, useCallback, useEffect, useInsertionEffect, useRef, useState} from 'react'
3
- import {Observable} from 'rxjs'
4
-
5
- const EMPTY_DEPS: DependencyList = []
6
-
7
- export function useObservableCallback<T, U>(
8
- fn: (arg: Observable<T>) => Observable<U>,
9
- dependencies: DependencyList = EMPTY_DEPS,
10
- ): (arg: T) => void {
11
- const callbackRef = useRef<[Observable<T>, (val: T) => void]>()
12
-
13
- if (!callbackRef.current) {
14
- callbackRef.current = observableCallback<T>()
15
- }
16
-
17
- const [calls$, call] = callbackRef.current
18
-
19
- const callback = useCallback(fn, dependencies)
20
-
21
- useEffect(() => {
22
- const subscription = calls$.pipe(callback).subscribe()
23
- return () => {
24
- subscription.unsubscribe()
25
- }
26
- }, [calls$, call, callback])
27
-
28
- return call
29
- }
2
+ import {useCallback, useEffect, useInsertionEffect, useRef, useState} from 'react'
3
+ import {type Observable} from 'rxjs'
30
4
 
5
+ /** @public */
31
6
  export function useObservableEvent<T, U>(
32
7
  fn: (arg: Observable<T>) => Observable<U>,
33
8
  ): (arg: T) => void {
@@ -46,7 +21,7 @@ export function useObservableEvent<T, U>(
46
21
  /**
47
22
  * This is a ponyfill of the upcoming `useEffectEvent` hook that'll arrive in React 19
48
23
  */
49
- export function useEffectEvent<
24
+ function useEffectEvent<
50
25
  const T extends (
51
26
  ...args: // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
27
  any[]
@@ -1 +0,0 @@
1
- export {};
@@ -1,96 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- var react_1 = require("@testing-library/react");
40
- var react_2 = require("react");
41
- var rxjs_1 = require("rxjs");
42
- var useObservable_1 = require("../useObservable");
43
- var wait = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
44
- // NOTE: Jest runs NODE_ENV=test by default, which enables development flags for React
45
- test('Strict mode should trigger double mount effects and re-renders', function () { return __awaiter(void 0, void 0, void 0, function () {
46
- function ObservableComponent() {
47
- (0, react_2.useEffect)(function () {
48
- mountCount++;
49
- }, []);
50
- var observedValue = (0, useObservable_1.useObservable)(observable);
51
- returnedValues.push(observedValue);
52
- return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
53
- }
54
- var subject, observable, returnedValues, mountCount;
55
- return __generator(this, function (_a) {
56
- switch (_a.label) {
57
- case 0:
58
- subject = new rxjs_1.BehaviorSubject(0);
59
- observable = subject.asObservable();
60
- returnedValues = [];
61
- mountCount = 0;
62
- (0, react_1.render)((0, react_2.createElement)(react_2.StrictMode, null, (0, react_2.createElement)(ObservableComponent)));
63
- expect(mountCount).toEqual(2);
64
- expect(returnedValues).toEqual([0, 0]);
65
- return [4 /*yield*/, wait(10)];
66
- case 1:
67
- _a.sent();
68
- (0, react_1.act)(function () { return subject.next(1); });
69
- expect(returnedValues).toEqual([0, 0, 1, 1]);
70
- (0, react_1.act)(function () { return subject.next(2); });
71
- expect(returnedValues).toEqual([0, 0, 1, 1, 2, 2]);
72
- expect(mountCount).toEqual(2);
73
- return [2 /*return*/];
74
- }
75
- });
76
- }); });
77
- test('Strict mode should unsubscribe the source observable on unmount', function () {
78
- var subscribed = [];
79
- var unsubscribed = [];
80
- var nextId = 0;
81
- var observable = new rxjs_1.Observable(function () {
82
- var id = nextId++;
83
- subscribed.push(id);
84
- return function () {
85
- unsubscribed.push(id);
86
- };
87
- });
88
- function ObservableComponent() {
89
- (0, useObservable_1.useObservable)(observable);
90
- return (0, react_2.createElement)(react_2.Fragment, null);
91
- }
92
- var rerender = (0, react_1.render)((0, react_2.createElement)(react_2.StrictMode, null, (0, react_2.createElement)(ObservableComponent))).rerender;
93
- expect(subscribed).toEqual([0, 1]);
94
- rerender((0, react_2.createElement)(react_2.StrictMode, null, (0, react_2.createElement)('div')));
95
- expect(unsubscribed).toEqual([0, 1]);
96
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,116 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- var react_1 = require("@testing-library/react");
4
- var react_2 = require("react");
5
- var rxjs_1 = require("rxjs");
6
- var operators_1 = require("rxjs/operators");
7
- var useObservable_1 = require("../useObservable");
8
- test('should subscribe immediately on component mount and unsubscribe on component unmount', function () {
9
- var subscribed = false;
10
- var observable = new rxjs_1.Observable(function () {
11
- subscribed = true;
12
- return function () {
13
- subscribed = false;
14
- };
15
- });
16
- expect(subscribed).toBe(false);
17
- var unmount = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }).unmount;
18
- expect(subscribed).toBe(true);
19
- unmount();
20
- expect(subscribed).toBe(false);
21
- });
22
- test('should only subscribe once when given same observable on re-renders', function () {
23
- var subscriptionCount = 0;
24
- var observable = new rxjs_1.Observable(function () {
25
- subscriptionCount++;
26
- });
27
- expect(subscriptionCount).toBe(0);
28
- var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }), unmount = _a.unmount, rerender = _a.rerender;
29
- expect(subscriptionCount).toBe(1);
30
- rerender();
31
- expect(subscriptionCount).toBe(1);
32
- unmount();
33
- (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); });
34
- expect(subscriptionCount).toBe(2);
35
- });
36
- test('should not return undefined during render if initial value is given', function () {
37
- var observable = (0, rxjs_1.timer)(100).pipe((0, operators_1.mapTo)('emitted value'));
38
- var returnedValues = [];
39
- function ObservableComponent() {
40
- var observedValue = (0, useObservable_1.useObservable)(observable, 'initial value');
41
- returnedValues.push(observedValue);
42
- return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
43
- }
44
- (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
45
- expect(returnedValues).toEqual(expect.arrayContaining(['initial value']));
46
- });
47
- test('should not return undefined during render if observable is sync', function () {
48
- var observable = (0, rxjs_1.of)('initial value');
49
- var returnedValues = [];
50
- function ObservableComponent() {
51
- var observedValue = (0, useObservable_1.useObservable)(observable);
52
- returnedValues.push(observedValue);
53
- return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
54
- }
55
- (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
56
- expect(returnedValues).toEqual(expect.arrayContaining(['initial value']));
57
- });
58
- test('should return undefined during first render if observable is async', function () {
59
- var observable = (0, rxjs_1.scheduled)('async value', rxjs_1.asyncScheduler);
60
- var returnedValues = [];
61
- function ObservableComponent() {
62
- var observedValue = (0, useObservable_1.useObservable)(observable);
63
- returnedValues.push(observedValue);
64
- return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
65
- }
66
- (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
67
- expect(returnedValues).toEqual(expect.arrayContaining([undefined]));
68
- });
69
- test('should have sync values from an observable as initial value', function () {
70
- var observable = (0, rxjs_1.of)('something sync');
71
- var result = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }).result;
72
- expect(result.current).toBe('something sync');
73
- });
74
- test('should have undefined as initial value from delayed observables', function () {
75
- var _a = (0, react_1.renderHook)(function () {
76
- return (0, useObservable_1.useObservable)((0, rxjs_1.scheduled)('something async', rxjs_1.asyncScheduler));
77
- }), result = _a.result, unmount = _a.unmount;
78
- expect(result.current).toBeUndefined();
79
- unmount();
80
- });
81
- test('should have passed initialValue as initial value from delayed observables', function () {
82
- var _a = (0, react_1.renderHook)(function () {
83
- return (0, useObservable_1.useObservable)((0, rxjs_1.scheduled)('something async', rxjs_1.asyncScheduler), 'initial');
84
- }), result = _a.result, unmount = _a.unmount;
85
- expect(result.current).toBe('initial');
86
- unmount();
87
- });
88
- test('should update with values from observables', function () {
89
- var values$ = new rxjs_1.Subject();
90
- var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(values$); }), result = _a.result, unmount = _a.unmount;
91
- expect(result.current).toBe(undefined);
92
- (0, react_1.act)(function () { return values$.next('something'); });
93
- expect(result.current).toBe('something');
94
- (0, react_1.act)(function () { return values$.next('otherthing'); });
95
- expect(result.current).toBe('otherthing');
96
- unmount();
97
- });
98
- test('should re-subscribe when receiving a new observable', function () {
99
- var first$ = new rxjs_1.Subject();
100
- var second$ = new rxjs_1.Subject();
101
- var current$ = first$;
102
- var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(current$, '!!initial!!'); }), result = _a.result, rerender = _a.rerender, unmount = _a.unmount;
103
- (0, react_1.act)(function () { return first$.next('first 1'); });
104
- expect(result.current).toBe('first 1');
105
- current$ = second$;
106
- rerender();
107
- // since observable #2 hasn't emitted a value yet, we should use the initial value
108
- expect(result.current).toBe('!!initial!!');
109
- // Now we should be subscribed to second$ and it's emission should be returned
110
- (0, react_1.act)(function () { return second$.next('second 1'); });
111
- expect(result.current).toBe('second 1');
112
- // we should no longer be subscribed to the first and ignore any emissions
113
- (0, react_1.act)(function () { return first$.next('first 2'); });
114
- expect(result.current).toBe('second 1');
115
- unmount();
116
- });
@@ -1,2 +0,0 @@
1
- export * from './useObservable';
2
- export * from './useObservableEvent';
package/dist/cjs/index.js DELETED
@@ -1,18 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./useObservable"), exports);
18
- __exportStar(require("./useObservableEvent"), exports);
@@ -1,4 +0,0 @@
1
- import type { Observable } from 'rxjs';
2
- export declare function useObservable<ObservableType extends Observable<any>>(observable: ObservableType, initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>)): UnboxObservable<ObservableType>;
3
- type UnboxObservable<T> = T extends Observable<infer U> ? U : never;
4
- export {};
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useObservable = void 0;
4
- var react_1 = require("react");
5
- var operators_1 = require("rxjs/operators");
6
- function getValue(value) {
7
- return typeof value === 'function' ? value() : value;
8
- }
9
- var cache = new WeakMap();
10
- function useObservable(observable, initialValue) {
11
- /**
12
- * Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
13
- * But we also don't want the initialValue to be stale if the observable changes.
14
- */
15
- var initialValueRef = (0, react_1.useRef)(getValue(initialValue));
16
- /**
17
- * Ensures that the initialValue is always up-to-date in case the observable changes.
18
- */
19
- (0, react_1.useEffect)(function () {
20
- initialValueRef.current = getValue(initialValue);
21
- }, [initialValue]);
22
- var store = (0, react_1.useMemo)(function () {
23
- if (!cache.has(observable)) {
24
- var entry_1 = {
25
- snapshot: initialValueRef.current,
26
- };
27
- entry_1.observable = observable.pipe((0, operators_1.shareReplay)({ refCount: true, bufferSize: 1 }), (0, operators_1.tap)(function (value) { return (entry_1.snapshot = value); }));
28
- // Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
29
- entry_1.subscription = entry_1.observable.subscribe();
30
- cache.set(observable, entry_1);
31
- }
32
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
33
- var instance = cache.get(observable);
34
- if (instance.subscription.closed) {
35
- instance.subscription = instance.observable.subscribe();
36
- }
37
- return {
38
- subscribe: function (onStoreChange) {
39
- var subscription = instance.observable.subscribe(onStoreChange);
40
- instance.subscription.unsubscribe();
41
- return function () { return subscription.unsubscribe(); };
42
- },
43
- getSnapshot: function () { return instance.snapshot; },
44
- };
45
- }, [observable]);
46
- return (0, react_1.useSyncExternalStore)(store.subscribe, store.getSnapshot);
47
- }
48
- exports.useObservable = useObservable;
@@ -1,8 +0,0 @@
1
- import { DependencyList } from 'react';
2
- import { Observable } from 'rxjs';
3
- export declare function useObservableCallback<T, U>(fn: (arg: Observable<T>) => Observable<U>, dependencies?: DependencyList): (arg: T) => void;
4
- export declare function useObservableEvent<T, U>(fn: (arg: Observable<T>) => Observable<U>): (arg: T) => void;
5
- /**
6
- * This is a ponyfill of the upcoming `useEffectEvent` hook that'll arrive in React 19
7
- */
8
- export declare function useEffectEvent<const T extends (...args: any[]) => void>(fn: T): T;