Skip to content

Commit b3bae7c

Browse files
Replace effect-based store sync with useSyncExternalStore
1 parent b0b2d6e commit b3bae7c

File tree

4 files changed

+46
-15
lines changed

4 files changed

+46
-15
lines changed

packages/react-bindings/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
],
2323
"dependencies": {
2424
"i18next": "^25.2.1",
25-
"rxjs": "~7.8.1"
25+
"rxjs": "~7.8.1",
26+
"use-sync-external-store": "^1.6.0"
2627
},
2728
"peerDependencies": {
2829
"@stream-io/video-client": "workspace:^",
@@ -32,6 +33,7 @@
3233
"@rollup/plugin-typescript": "^12.1.2",
3334
"@stream-io/video-client": "workspace:^",
3435
"@types/react": "^19.2.0",
36+
"@types/use-sync-external-store": "^1",
3537
"react": "19.0.0",
3638
"rimraf": "^6.0.1",
3739
"rollup": "^4.40.2",

packages/react-bindings/src/hooks/callStateHooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ function useLazyDeviceList(manager: DeviceManagerLike) {
525525
setDevices$(manager.listDevices());
526526
}
527527

528-
return devices;
528+
return devices ?? EMPTY_DEVICES_ARRAY;
529529
};
530530

531531
return { getDevices };
Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
11
import type { Observable } from 'rxjs';
2-
import { useEffect, useState } from 'react';
2+
import { useCallback } from 'react';
3+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
34
import { RxUtils } from '@stream-io/video-client';
45

56
/**
67
* Utility hook which provides the current value of the given observable.
78
*
89
* @param observable$ the observable to read data from.
9-
* @param defaultValue a default value. Used when the observable data can't be read or emits an error.
10+
* @param defaultValue a default value. Used when the observable data can't be read or emits an error - must be stable.
1011
*/
1112
export const useObservableValue = <T>(
1213
observable$: Observable<T>,
1314
defaultValue?: T,
1415
) => {
15-
const [value, setValue] = useState<T>(() => {
16+
const getSnapshot = useCallback(() => {
1617
try {
1718
return RxUtils.getCurrentValue(observable$);
18-
} catch (err) {
19-
if (typeof defaultValue === 'undefined') throw err;
19+
} catch (error) {
20+
if (typeof defaultValue === 'undefined') throw error;
2021
return defaultValue;
2122
}
22-
});
23-
24-
useEffect(() => {
25-
return RxUtils.createSubscription(observable$, setValue, (err) => {
26-
console.log('An error occurred while reading an observable', err);
27-
if (defaultValue) setValue(defaultValue);
28-
});
2923
}, [defaultValue, observable$]);
3024

31-
return value;
25+
const subscribe = useCallback(
26+
(onStoreChange: (v: T) => void) => {
27+
const unsubscribe = RxUtils.createSubscription(
28+
observable$,
29+
onStoreChange,
30+
(error) => {
31+
console.log('An error occurred while reading an observable', error);
32+
33+
if (defaultValue) onStoreChange(defaultValue);
34+
},
35+
);
36+
37+
return unsubscribe;
38+
},
39+
[defaultValue, observable$],
40+
);
41+
42+
return useSyncExternalStore(subscribe, getSnapshot);
3243
};

yarn.lock

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8763,12 +8763,14 @@ __metadata:
87638763
"@rollup/plugin-typescript": "npm:^12.1.2"
87648764
"@stream-io/video-client": "workspace:^"
87658765
"@types/react": "npm:^19.2.0"
8766+
"@types/use-sync-external-store": "npm:^1"
87668767
i18next: "npm:^25.2.1"
87678768
react: "npm:19.0.0"
87688769
rimraf: "npm:^6.0.1"
87698770
rollup: "npm:^4.40.2"
87708771
rxjs: "npm:~7.8.1"
87718772
typescript: "npm:^5.8.3"
8773+
use-sync-external-store: "npm:^1.6.0"
87728774
peerDependencies:
87738775
"@stream-io/video-client": "workspace:^"
87748776
react: ^17 || ^18 || ^19
@@ -9658,6 +9660,13 @@ __metadata:
96589660
languageName: node
96599661
linkType: hard
96609662

9663+
"@types/use-sync-external-store@npm:^1":
9664+
version: 1.5.0
9665+
resolution: "@types/use-sync-external-store@npm:1.5.0"
9666+
checksum: 10/39e5be8dc2cca080b490f2f79fed4381ae7eebee3f981208e359856733eafb2479d229db07a552f6c99fe0b5c09b3e46a3e6a870e00a88b50f3e690e73d2649b
9667+
languageName: node
9668+
linkType: hard
9669+
96619670
"@types/ws@npm:^8.5.14":
96629671
version: 8.18.1
96639672
resolution: "@types/ws@npm:8.18.1"
@@ -27008,6 +27017,15 @@ __metadata:
2700827017
languageName: node
2700927018
linkType: hard
2701027019

27020+
"use-sync-external-store@npm:^1.6.0":
27021+
version: 1.6.0
27022+
resolution: "use-sync-external-store@npm:1.6.0"
27023+
peerDependencies:
27024+
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
27025+
checksum: 10/b40ad2847ba220695bff2d4ba4f4d60391c0fb4fb012faa7a4c18eb38b69181936f5edc55a522c4d20a788d1a879b73c3810952c9d0fd128d01cb3f22042c09e
27026+
languageName: node
27027+
linkType: hard
27028+
2701127029
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1":
2701227030
version: 1.0.2
2701327031
resolution: "util-deprecate@npm:1.0.2"

0 commit comments

Comments
 (0)