Skip to content

Commit 284d7e9

Browse files
committed
refactor: use event-target-bus/react
1 parent 1afb803 commit 284d7e9

5 files changed

Lines changed: 24 additions & 79 deletions

File tree

packages/foxact/src/use-is-online/index.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
11
'use client';
22

33
import { useSyncExternalStore } from 'react';
4-
import { createEventTargetBus } from 'event-target-bus';
5-
import type { EventTargetBus } from 'event-target-bus';
4+
import { createSyncExternalStoreSubscribe } from 'event-target-bus/react';
65

7-
let onlineBus: EventTargetBus<Window, 'online'> | null = null;
8-
let offlineBus: EventTargetBus<Window, 'offline'> | null = null;
9-
10-
function subscribe(onStoreChange: () => void): () => void {
11-
onlineBus ??= createEventTargetBus(window, 'online');
12-
offlineBus ??= createEventTargetBus(window, 'offline');
13-
14-
const onlineUnsub = onlineBus.on(onStoreChange);
15-
const offlineUnsub = offlineBus.on(onStoreChange);
16-
17-
return () => {
18-
onlineUnsub();
19-
offlineUnsub();
20-
};
21-
}
6+
const subscribe = createSyncExternalStoreSubscribe(
7+
() => window,
8+
['online', 'offline']
9+
);
2210

2311
function getSnapshot() {
2412
if (typeof window === 'undefined') {

packages/foxact/src/use-media-query/index.ts

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,14 @@
22
'use client';
33

44
import { noSSRError } from '../no-ssr';
5-
import { noop } from '../noop';
65

7-
import { useCallback, useSyncExternalStore } from 'react';
8-
import { createEventTargetBus } from 'event-target-bus';
9-
import type { EventTargetBus } from 'event-target-bus';
6+
import { useMemo, useSyncExternalStore } from 'react';
7+
import { createKeyedSyncExternalStoreSubscribe } from 'event-target-bus/react';
108

11-
const mediaQueryProxies = new Map<string, EventTargetBus<MediaQueryList, 'change'>>();
12-
13-
function subscribeToMediaQuery(mq: string, callback: VoidFunction) {
14-
/* istanbul ignore if -- SSR-only guard, unreachable when Happy DOM registers window globally in tests */
15-
if (typeof window === 'undefined') return noop;
16-
17-
let bus = mediaQueryProxies.get(mq);
18-
if (!bus) {
19-
bus = createEventTargetBus(window.matchMedia(mq), 'change');
20-
mediaQueryProxies.set(mq, bus);
21-
}
22-
23-
return bus.on(callback);
24-
}
9+
const getMediaQuerySubscribe = createKeyedSyncExternalStoreSubscribe(
10+
(mq: string) => window.matchMedia(mq),
11+
'change'
12+
);
2513

2614
function getServerSnapshotWithoutServerValue(): never {
2715
throw noSSRError('useMediaQuery cannot be used on the server without a serverValue');
@@ -30,9 +18,6 @@ function getServerSnapshotWithoutServerValue(): never {
3018
/** @see https://foxact.skk.moe/use-media-query */
3119
// eslint-disable-next-line sukka/bool-param-default -- serveValue is intentionally optional
3220
export function useMediaQuery(mq: string, serverValue?: boolean): boolean {
33-
// subscribe once per hook per media query
34-
const subscribe = useCallback((callback: VoidFunction) => subscribeToMediaQuery(mq, callback), [mq]);
35-
3621
const getSnapshot = () => {
3722
/* istanbul ignore if -- SSR-only guard, unreachable when Happy DOM registers window globally in tests */
3823
if (typeof window === 'undefined') {
@@ -48,5 +33,8 @@ export function useMediaQuery(mq: string, serverValue?: boolean): boolean {
4833
? getServerSnapshotWithoutServerValue
4934
: () => serverValue;
5035

36+
// ensure stableness per mq
37+
const subscribe = useMemo(() => getMediaQuerySubscribe(mq), [mq]);
38+
5139
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); // Use useSyncExternalStore to manage the subscription and state
5240
}

packages/foxact/src/use-page-visibility/index.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
'use client';
22

33
import { useSyncExternalStore } from 'react';
4-
import { createEventTargetBus } from 'event-target-bus';
5-
import type { EventTargetBus } from 'event-target-bus';
6-
import { noop } from '../noop';
4+
import { createSyncExternalStoreSubscribe } from 'event-target-bus/react';
75

8-
let visibilityChangeBus: EventTargetBus<Document, 'visibilitychange'> | null = null;
9-
10-
const handlePageVisibilityChange: Parameters<typeof useSyncExternalStore>[0] = (onChange) => {
11-
/* istanbul ignore if -- SSR-only guard, unreachable when Happy DOM registers window globally in tests */
12-
if (typeof window === 'undefined') return noop;
13-
14-
visibilityChangeBus ??= createEventTargetBus(document, 'visibilitychange');
15-
16-
return visibilityChangeBus.on(onChange);
17-
};
6+
const subscribe = createSyncExternalStoreSubscribe(
7+
() => document,
8+
'visibilitychange'
9+
);
1810

1911
const getSnapshot: Parameters<typeof useSyncExternalStore>[1] = () => {
2012
/* istanbul ignore if -- SSR-only guard, unreachable when Happy DOM registers document globally in tests */
@@ -28,7 +20,7 @@ const getSnapshot: Parameters<typeof useSyncExternalStore>[1] = () => {
2820
/** @see https://foxact.skk.moe/use-page-visibility */
2921
export function usePageVisibility() {
3022
return useSyncExternalStore(
31-
handlePageVisibilityChange,
23+
subscribe,
3224
getSnapshot,
3325
getSnapshot
3426
);

packages/foxact/src/use-readonly-search-params/index.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { createEventTargetBus } from 'event-target-bus';
2-
import type { EventTargetBus } from 'event-target-bus';
1+
import { createSyncExternalStoreSubscribe } from 'event-target-bus/react';
32
import { noSSRError } from '../no-ssr';
4-
import { noop } from '../noop';
53
import { useSyncExternalStore } from 'react';
64

75
class ReadonlyURLSearchParamsError extends Error {
@@ -39,16 +37,7 @@ export class ReadonlyURLSearchParams extends URLSearchParams {
3937
}
4038
}
4139

42-
let popStateBus: EventTargetBus<Window, 'popstate'> | null = null;
43-
44-
function subscribe(onStoreChange: () => void) {
45-
/* istanbul ignore if -- SSR-only guard, unreachable when Happy DOM registers window globally in tests */
46-
if (typeof window === 'undefined') return noop;
47-
48-
popStateBus ??= createEventTargetBus(window, 'popstate');
49-
50-
return popStateBus.on(onStoreChange);
51-
}
40+
const subscribe = createSyncExternalStoreSubscribe(() => window, 'popstate');
5241

5342
let lastSearch: string | null = null;
5443
let lastUrlSearchParams: ReadonlyURLSearchParams | null = null;

packages/foxact/src/use-url-hash-state/index.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,16 @@
11
import 'client-only';
22

33
import { useCallback, useMemo, useSyncExternalStore } from 'react';
4-
import { noop } from '../noop';
54
import { noSSRError } from '../no-ssr';
65

76
import { identity } from 'foxts/identity';
87
import { isFunction } from 'foxts/is-function';
9-
import { createEventTargetBus } from 'event-target-bus';
10-
import type { EventTargetBus } from 'event-target-bus';
8+
import { createSyncExternalStoreSubscribe } from 'event-target-bus/react';
119

12-
let hashChangeEventBus: EventTargetBus<Window, 'hashchange'> | null = null;
10+
const subscribe = createSyncExternalStoreSubscribe(() => window, 'hashchange');
1311

1412
type NotUndefined<T> = T extends undefined ? never : T;
1513

16-
const subscribe: Parameters<typeof useSyncExternalStore>[0] = (onStoreChange) => {
17-
if (typeof window === 'undefined') {
18-
return noop;
19-
}
20-
21-
hashChangeEventBus ??= createEventTargetBus(window, 'hashchange');
22-
23-
return hashChangeEventBus.on(onStoreChange);
24-
};
25-
2614
export type Serializer<T> = (value: T) => string;
2715
export type Deserializer<T> = (value: string) => T;
2816

0 commit comments

Comments
 (0)