Skip to content

Commit 5265901

Browse files
committed
main 🧊 update use query, use websocket
1 parent 00db168 commit 5265901

13 files changed

Lines changed: 272 additions & 141 deletions

File tree

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@siberiacancode/reactuse",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"description": "The ultimate collection of react hooks",
55
"author": {
66
"name": "SIBERIA CAN CODE 🧊",

packages/core/src/bundle/hooks/useDeviceList/useDeviceList.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,22 @@ export const useDeviceList = (...params) => {
4141
optionsRef.current?.onUpdate?.(list);
4242
return list;
4343
};
44-
const trigger = async () => {
44+
const trigger = async (constraints) => {
4545
if (!supported) return;
4646
const list = await navigator.mediaDevices.enumerateDevices();
4747
const hasCamera = list.some((device) => device.kind === 'videoinput');
48-
const hasMicrophone = list.some((device) => device.kind === 'audioinput');
49-
if (!hasCamera && !hasMicrophone) return update();
50-
try {
51-
const stream = await navigator.mediaDevices.getUserMedia({
52-
video: hasCamera,
53-
audio: hasMicrophone
54-
});
55-
stream.getTracks().forEach((track) => track.stop());
56-
} catch {}
57-
setDevices(list);
58-
optionsRef.current?.onUpdate?.(list);
59-
return list;
48+
const hasMicrophone = list.some(
49+
(device) => device.kind === 'audioinput' || device.kind === 'audiooutput'
50+
);
51+
const video = constraints?.video ?? optionsRef.current?.constraints?.video ?? hasCamera;
52+
const audio = constraints?.video ?? optionsRef.current?.constraints?.audio ?? hasMicrophone;
53+
if (video || audio) {
54+
try {
55+
const stream = await navigator.mediaDevices.getUserMedia({ video, audio });
56+
stream.getTracks().forEach((track) => track.stop());
57+
} catch {}
58+
}
59+
return update();
6060
};
6161
useEffect(() => {
6262
if (!supported) return;

packages/core/src/bundle/hooks/useQuery/useQuery.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,18 @@ import { useMount } from '../useMount/useMount';
1414
* @param {(data: Data) => void} [options.onSuccess] The callback function to be invoked on success
1515
* @param {(error: Error) => void} [options.onError] The callback function to be invoked on error
1616
* @param {UseQueryOptionsSelect<Data>} [options.select] The select function to be invoked
17-
* @param {Data | (() => Data)} [options.initialData] The initial data for the hook
1817
* @param {Data | (() => Data)} [options.placeholderData] The placeholder data for the hook
1918
* @param {number} [options.refetchInterval] The refetch interval
20-
* @param {boolean | number} [options.retry] The retry count of requests
19+
* @param {boolean | number | ((failureCount: number, error: Error) => boolean)} [options.retry] The retry count of requests, or a function to decide whether to retry
2120
* @returns {UseQueryReturn<Data>} An object with the state of the query
2221
*
2322
* @example
24-
* const { data, isFetching, isLoading, isError, isSuccess, error, refetch, isRefetching, abort, aborted } = useQuery(() => fetch('url'));
23+
* const { data, isFetching, isLoading, isError, isSuccess, error, refetch, isRefetching, abort } = useQuery(() => fetch('url'));
2524
*/
2625
export const useQuery = (callback, options) => {
2726
const enabled = options?.enabled ?? true;
2827
const canRequestOnMount = enabled && typeof window !== 'undefined';
29-
const retryCountRef = useRef(options?.retry ? getRetry(options.retry) : 0);
28+
const failureCountRef = useRef(0);
3029
const alreadyRequestedRef = useRef(false);
3130
const [isFetching, setIsFetching] = useState(canRequestOnMount);
3231
const [isLoading, setIsLoading] = useState(canRequestOnMount);
@@ -54,6 +53,7 @@ export const useQuery = (callback, options) => {
5453
.then((response) => {
5554
const data = options?.select ? options?.select(response) : response;
5655
options?.onSuccess?.(data);
56+
failureCountRef.current = 0;
5757
setData(data);
5858
setIsSuccess(true);
5959
setError(undefined);
@@ -63,35 +63,35 @@ export const useQuery = (callback, options) => {
6363
if (action === 'refetch') setIsRefetching(false);
6464
})
6565
.catch((error) => {
66-
if (retryCountRef.current > 0) {
67-
retryCountRef.current -= 1;
68-
const retryDelay =
69-
typeof options?.retryDelay === 'function'
70-
? options?.retryDelay(retryCountRef.current, error)
71-
: options?.retryDelay;
72-
if (retryDelay) {
73-
setTimeout(request, retryDelay, action);
74-
return;
75-
}
66+
if (
67+
typeof options?.retry === 'function'
68+
? options.retry(failureCountRef.current, error)
69+
: failureCountRef.current < getRetry(options?.retry ?? 0)
70+
) {
71+
failureCountRef.current += 1;
7672
request(action);
7773
return;
7874
}
7975
options?.onError?.(error);
76+
failureCountRef.current = 0;
8077
setData(undefined);
8178
setIsSuccess(false);
8279
setError(error);
8380
setIsError(true);
8481
setIsFetching(false);
8582
if (action === 'init') setIsLoading(false);
8683
if (action === 'refetch') setIsRefetching(false);
87-
retryCountRef.current = options?.retry ? getRetry(options.retry) : 0;
8884
})
8985
.finally(() => {
90-
if (options?.refetchInterval) {
86+
const refetchInterval =
87+
typeof options?.refetchInterval === 'function'
88+
? options.refetchInterval()
89+
: options?.refetchInterval;
90+
if (refetchInterval) {
9191
const interval = setInterval(() => {
9292
clearInterval(interval);
9393
request('refetch');
94-
}, options?.refetchInterval);
94+
}, refetchInterval);
9595
intervalIdRef.current = interval;
9696
}
9797
});
@@ -108,7 +108,7 @@ export const useQuery = (callback, options) => {
108108
() => () => {
109109
clearInterval(intervalIdRef.current);
110110
},
111-
[enabled, options?.refetchInterval, options?.retry, ...keys]
111+
[enabled, options?.retry, ...keys]
112112
);
113113
const refetch = () => {
114114
request('refetch');

packages/core/src/bundle/hooks/useWebSocket/useWebSocket.js

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,30 @@ import { getRetry } from '@/utils/helpers';
1010
*
1111
* @param {UseWebSocketUrl} url The URL of the WebSocket server
1212
* @param {(webSocket: WebSocket) => void} [options.onConnected] The callback function that is called when the WebSocket connection is established
13-
* @param {(event: CloseEvent, webSocket: WebSocket) => void} [options.onDisconnected] The callback function that is called when the WebSocket connection is closed
13+
* @param {(event: CloseEvent, webSocket: WebSocket) => void} [options.onClose] The callback function that is called when the WebSocket connection is closed
1414
* @param {(event: Event, webSocket: WebSocket) => void} [options.onError] The callback function that is called when an error occurs
1515
* @param {(event: MessageEvent, webSocket: WebSocket) => void} [options.onMessage] The callback function that is called when a message is received
16-
* @param {boolean | number} [options.retry] The number of times to retry the connection
16+
* @param {boolean} [options.immediately=true] Immediately open the connection when calling this hook
17+
* @param {boolean | number | ((failureCount: number, event: CloseEvent) => boolean)} [options.retry] The number of times to retry the connection, or a function to decide whether to retry
1718
* @param {Array<'soap' | 'wasm'>} [options.protocols] The list of protocols to use
18-
* @returns {UseWebSocketReturn} An object with the status, close, send, open, and ws properties
19+
* @returns {UseWebSocketReturn} An object with the status, close, send, open, and client properties
1920
*
2021
* @example
2122
* const { status, close, send, open, client } = useWebSocket('url');
2223
*/
2324
export const useWebSocket = (url, options) => {
25+
const immediately = options?.immediately ?? true;
2426
const webSocketRef = useRef(undefined);
25-
const retryCountRef = useRef(options?.retry ? getRetry(options.retry) : 0);
27+
const failureCountRef = useRef(0);
2628
const explicityCloseRef = useRef(false);
27-
const [status, setStatus] = useState('connecting');
29+
const [status, setStatus] = useState(immediately ? 'connecting' : 'disconnected');
2830
const send = (data) => {
2931
webSocketRef.current?.send(data);
3032
};
3133
const close = () => {
3234
explicityCloseRef.current = true;
3335
webSocketRef.current?.close();
36+
webSocketRef.current = undefined;
3437
};
3538
const init = () => {
3639
webSocketRef.current = new WebSocket(
@@ -41,6 +44,7 @@ export const useWebSocket = (url, options) => {
4144
const webSocket = webSocketRef.current;
4245
if (!webSocket) return;
4346
webSocket.onopen = () => {
47+
failureCountRef.current = 0;
4448
setStatus('connected');
4549
options?.onConnected?.(webSocket);
4650
};
@@ -51,26 +55,36 @@ export const useWebSocket = (url, options) => {
5155
webSocket.onmessage = (event) => options?.onMessage?.(event, webSocket);
5256
webSocket.onclose = (event) => {
5357
setStatus('disconnected');
54-
options?.onDisconnected?.(event, webSocket);
58+
options?.onClose?.(event, webSocket);
5559
if (explicityCloseRef.current) return;
56-
if (retryCountRef.current > 0) {
57-
retryCountRef.current -= 1;
60+
const shouldRetry =
61+
typeof options?.retry === 'function'
62+
? options.retry(failureCountRef.current, event)
63+
: failureCountRef.current < getRetry(options?.retry ?? 0);
64+
if (shouldRetry) {
65+
failureCountRef.current += 1;
5866
return init();
5967
}
60-
retryCountRef.current = options?.retry ? getRetry(options.retry) : 0;
68+
failureCountRef.current = 0;
6169
};
6270
};
63-
useEffect(() => {
71+
const open = () => {
72+
explicityCloseRef.current = false;
73+
if (webSocketRef.current) {
74+
webSocketRef.current.onclose = null;
75+
webSocketRef.current.close();
76+
webSocketRef.current = undefined;
77+
}
6478
init();
79+
};
80+
useEffect(() => {
81+
if (immediately) init();
6582
return () => {
6683
if (!webSocketRef.current) return;
84+
webSocketRef.current.onclose = null;
6785
webSocketRef.current.close();
6886
webSocketRef.current = undefined;
6987
};
7088
}, [url]);
71-
const open = () => {
72-
explicityCloseRef.current = false;
73-
init();
74-
};
7589
return { client: webSocketRef.current, close, open, send, status };
7690
};

packages/core/src/hooks/useDeviceList/useDeviceList.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ it('Should load devices immediately', async () => {
112112
audio: true,
113113
video: true
114114
});
115-
expect(mockEnumerateDevices).toHaveBeenCalledOnce();
115+
expect(mockEnumerateDevices).toHaveBeenCalledTimes(2);
116116
expect(mockStop).toHaveBeenCalledTimes(1);
117117
});
118118

@@ -151,7 +151,7 @@ it('Should trigger device permissions and update devices by action', async () =>
151151
audio: true,
152152
video: true
153153
});
154-
expect(mockEnumerateDevices).toHaveBeenCalledOnce();
154+
expect(mockEnumerateDevices).toHaveBeenCalledTimes(2);
155155
expect(result.current.devices).toEqual(devices);
156156
expect(mockStop).toHaveBeenCalledTimes(1);
157157
});

packages/core/src/hooks/useDeviceList/useDeviceList.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export type UseDeviceListCallback = (devices: MediaDeviceInfo[]) => void;
55

66
/** The use device list options type */
77
export interface UseDeviceListOptions {
8+
/** The constraints passed to `getUserMedia` when requesting permissions */
9+
constraints?: MediaStreamConstraints;
810
/** Whether the device list should be requested immediately */
911
immediately?: boolean;
1012
/** The callback fired when the device list updates */
@@ -87,25 +89,26 @@ export const useDeviceList = ((...params: any[]) => {
8789
return list;
8890
};
8991

90-
const trigger = async () => {
92+
const trigger = async (constraints?: MediaStreamConstraints) => {
9193
if (!supported) return;
9294

9395
const list = await navigator.mediaDevices.enumerateDevices();
9496
const hasCamera = list.some((device) => device.kind === 'videoinput');
95-
const hasMicrophone = list.some((device) => device.kind === 'audioinput');
96-
if (!hasCamera && !hasMicrophone) return update();
97+
const hasMicrophone = list.some(
98+
(device) => device.kind === 'audioinput' || device.kind === 'audiooutput'
99+
);
97100

98-
try {
99-
const stream = await navigator.mediaDevices.getUserMedia({
100-
video: hasCamera,
101-
audio: hasMicrophone
102-
});
103-
stream.getTracks().forEach((track) => track.stop());
104-
} catch {}
101+
const video = constraints?.video ?? optionsRef.current?.constraints?.video ?? hasCamera;
102+
const audio = constraints?.video ?? optionsRef.current?.constraints?.audio ?? hasMicrophone;
105103

106-
setDevices(list);
107-
optionsRef.current?.onUpdate?.(list);
108-
return list;
104+
if (video || audio) {
105+
try {
106+
const stream = await navigator.mediaDevices.getUserMedia({ video, audio });
107+
stream.getTracks().forEach((track) => track.stop());
108+
} catch {}
109+
}
110+
111+
return update();
109112
};
110113

111114
useEffect(() => {

0 commit comments

Comments
 (0)