react-rx 3.0.0-5 → 3.0.0-6

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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Sanity.io
3
+ Copyright (c) 2024 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.mts CHANGED
@@ -1,13 +1,11 @@
1
1
  import {Observable} from 'rxjs'
2
-
3
- /** @internal */
4
- export declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never
2
+ import type {ObservedValueOf} from 'rxjs'
5
3
 
6
4
  /** @public */
7
5
  export declare function useObservable<ObservableType extends Observable<any>>(
8
6
  observable: ObservableType,
9
- initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
10
- ): UnboxObservable<ObservableType>
7
+ initialValue?: ObservedValueOf<ObservableType> | (() => ObservedValueOf<ObservableType>),
8
+ ): ObservedValueOf<ObservableType>
11
9
 
12
10
  /** @public */
13
11
  export declare function useObservableEvent<T, U>(
package/dist/index.d.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import {Observable} from 'rxjs'
2
-
3
- /** @internal */
4
- export declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never
2
+ import type {ObservedValueOf} from 'rxjs'
5
3
 
6
4
  /** @public */
7
5
  export declare function useObservable<ObservableType extends Observable<any>>(
8
6
  observable: ObservableType,
9
- initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
10
- ): UnboxObservable<ObservableType>
7
+ initialValue?: ObservedValueOf<ObservableType> | (() => ObservedValueOf<ObservableType>),
8
+ ): ObservedValueOf<ObservableType>
11
9
 
12
10
  /** @public */
13
11
  export declare function useObservableEvent<T, U>(
package/dist/index.js.map CHANGED
@@ -1 +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;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/useObservable.ts","../src/useObservableEvent.ts"],"sourcesContent":["import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'\nimport type {Observable, ObservedValueOf, 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?: ObservedValueOf<ObservableType> | (() => ObservedValueOf<ObservableType>),\n): ObservedValueOf<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 ObservedValueOf<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 ObservedValueOf<ObservableType>\n }, [initialValue])\n\n const store = useMemo(() => {\n if (!cache.has(observable)) {\n const entry: Partial<CacheRecord<ObservedValueOf<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<ObservedValueOf<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<ObservedValueOf<ObservableType>>(store.subscribe, store.getSnapshot)\n}\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;;;"}
@@ -1 +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;"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/useObservable.ts","../src/useObservableEvent.ts"],"sourcesContent":["import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'\nimport type {Observable, ObservedValueOf, 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?: ObservedValueOf<ObservableType> | (() => ObservedValueOf<ObservableType>),\n): ObservedValueOf<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 ObservedValueOf<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 ObservedValueOf<ObservableType>\n }, [initialValue])\n\n const store = useMemo(() => {\n if (!cache.has(observable)) {\n const entry: Partial<CacheRecord<ObservedValueOf<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<ObservedValueOf<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<ObservedValueOf<ObservableType>>(store.subscribe, store.getSnapshot)\n}\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-5",
3
+ "version": "3.0.0-6",
4
4
  "description": "React + RxJS = <3",
5
5
  "keywords": [
6
6
  "action",
@@ -15,6 +15,7 @@
15
15
  "pipe",
16
16
  "react",
17
17
  "react18",
18
+ "react19",
18
19
  "reactive",
19
20
  "realtime",
20
21
  "rx",
@@ -28,7 +29,6 @@
28
29
  "sync",
29
30
  "typesafe",
30
31
  "typescript",
31
- "use-sync-external-store",
32
32
  "use"
33
33
  ],
34
34
  "homepage": "https://react-rx.dev",
@@ -71,7 +71,7 @@
71
71
  "dev": "cd website && npm run dev",
72
72
  "prepublishOnly": "npm run build",
73
73
  "watch": "npm run build -- --watch",
74
- "test": "jest",
74
+ "test": "vitest run",
75
75
  "lint": "eslint --cache ."
76
76
  },
77
77
  "browserslist": "extends @sanity/browserslist-config",
@@ -80,12 +80,11 @@
80
80
  "observable-callback": "^1.0.3"
81
81
  },
82
82
  "devDependencies": {
83
- "@sanity/pkg-utils": "^6.9.2",
83
+ "@sanity/pkg-utils": "^6.9.3",
84
84
  "@sanity/prettier-config": "^1.0.2",
85
- "@sanity/semantic-release-preset": "^2.0.5",
85
+ "@sanity/semantic-release-preset": "^4.1.8",
86
86
  "@testing-library/dom": "^10.1.0",
87
87
  "@testing-library/react": "^16.0.0",
88
- "@types/jest": "^29.5.3",
89
88
  "@types/node": "^18.17.5",
90
89
  "@types/react": "^18.3.3",
91
90
  "@types/react-dom": "^18.3.0",
@@ -98,17 +97,15 @@
98
97
  "eslint-plugin-react-compiler": "0.0.0-experimental-51a85ea-20240601",
99
98
  "eslint-plugin-react-hooks": "^4.6.2",
100
99
  "eslint-plugin-simple-import-sort": "^12.1.0",
101
- "jest": "^29.6.2",
102
- "jest-environment-jsdom": "^29.6.2",
103
- "jsdom": "^20.0.3",
104
100
  "npm-run-all": "^4.1.5",
105
101
  "prettier": "^3.3.1",
106
102
  "react": "^18.3.1",
107
103
  "react-dom": "^18.3.1",
108
104
  "react-test-renderer": "^18.3.1",
109
105
  "rxjs": "^7.8.1",
110
- "ts-jest": "^29.1.1",
111
- "typescript": "5.4.5"
106
+ "semantic-release": "^24.0.0",
107
+ "typescript": "5.4.5",
108
+ "vitest": "^1.6.0"
112
109
  },
113
110
  "peerDependencies": {
114
111
  "react": "^18.3 || >=19.0.0-rc",
@@ -1,6 +1,7 @@
1
1
  import {act, render} from '@testing-library/react'
2
2
  import {createElement, Fragment, StrictMode, useEffect} from 'react'
3
3
  import {BehaviorSubject, Observable} from 'rxjs'
4
+ import {expect, test} from 'vitest'
4
5
 
5
6
  import {useObservable} from '../useObservable'
6
7
 
@@ -2,6 +2,7 @@ import {act, render, renderHook} from '@testing-library/react'
2
2
  import {createElement, Fragment} from 'react'
3
3
  import {asyncScheduler, Observable, of, scheduled, Subject, timer} from 'rxjs'
4
4
  import {mapTo} from 'rxjs/operators'
5
+ import {expect, test} from 'vitest'
5
6
 
6
7
  import {useObservable} from '../useObservable'
7
8
 
@@ -1,5 +1,5 @@
1
1
  import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'
2
- import type {Observable, Subscription} from 'rxjs'
2
+ import type {Observable, ObservedValueOf, Subscription} from 'rxjs'
3
3
  import {shareReplay, tap} from 'rxjs/operators'
4
4
 
5
5
  function getValue<T>(value: T): T extends () => infer U ? U : T {
@@ -16,24 +16,24 @@ const cache = new WeakMap<Observable<any>, CacheRecord<any>>()
16
16
  /** @public */
17
17
  export function useObservable<ObservableType extends Observable<any>>(
18
18
  observable: ObservableType,
19
- initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
20
- ): UnboxObservable<ObservableType> {
19
+ initialValue?: ObservedValueOf<ObservableType> | (() => ObservedValueOf<ObservableType>),
20
+ ): ObservedValueOf<ObservableType> {
21
21
  /**
22
22
  * Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
23
23
  * But we also don't want the initialValue to be stale if the observable changes.
24
24
  */
25
- const initialValueRef = useRef(getValue(initialValue) as UnboxObservable<ObservableType>)
25
+ const initialValueRef = useRef(getValue(initialValue) as ObservedValueOf<ObservableType>)
26
26
 
27
27
  /**
28
28
  * Ensures that the initialValue is always up-to-date in case the observable changes.
29
29
  */
30
30
  useEffect(() => {
31
- initialValueRef.current = getValue(initialValue) as UnboxObservable<ObservableType>
31
+ initialValueRef.current = getValue(initialValue) as ObservedValueOf<ObservableType>
32
32
  }, [initialValue])
33
33
 
34
34
  const store = useMemo(() => {
35
35
  if (!cache.has(observable)) {
36
- const entry: Partial<CacheRecord<UnboxObservable<ObservableType>>> = {
36
+ const entry: Partial<CacheRecord<ObservedValueOf<ObservableType>>> = {
37
37
  snapshot: initialValueRef.current,
38
38
  }
39
39
  entry.observable = observable.pipe(
@@ -44,7 +44,7 @@ export function useObservable<ObservableType extends Observable<any>>(
44
44
  // Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
45
45
  entry.subscription = entry.observable.subscribe()
46
46
 
47
- cache.set(observable, entry as CacheRecord<UnboxObservable<ObservableType>>)
47
+ cache.set(observable, entry as CacheRecord<ObservedValueOf<ObservableType>>)
48
48
  }
49
49
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
50
50
  const instance = cache.get(observable)!
@@ -62,8 +62,5 @@ export function useObservable<ObservableType extends Observable<any>>(
62
62
  }
63
63
  }, [observable])
64
64
 
65
- return useSyncExternalStore<UnboxObservable<ObservableType>>(store.subscribe, store.getSnapshot)
65
+ return useSyncExternalStore<ObservedValueOf<ObservableType>>(store.subscribe, store.getSnapshot)
66
66
  }
67
-
68
- /** @internal */
69
- export type UnboxObservable<T> = T extends Observable<infer U> ? U : never