Skip to content

Commit a8fc4ea

Browse files
Replace effect-based store sync with useSyncExternalStore
1 parent e47bf8b commit a8fc4ea

File tree

4 files changed

+125
-513
lines changed

4 files changed

+125
-513
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
};

0 commit comments

Comments
 (0)