Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 68 additions & 66 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createUpdateEffect } from './createUpdateEffect';
import useAntdTable from './useAntdTable';
import useAsyncEffect from './useAsyncEffect';
import useBoolean from './useBoolean';
import useBroadcastChannel from './useBroadcastChannel';
import useClickAway from './useClickAway';
import useControllableValue from './useControllableValue';
import useCookieState from './useCookieState';
Expand Down Expand Up @@ -41,6 +42,7 @@ import useMap from './useMap';
import useMemoizedFn from './useMemoizedFn';
import useMount from './useMount';
import useMouse from './useMouse';
import useMutationObserver from './useMutationObserver';
import useNetwork from './useNetwork';
import usePagination from './usePagination';
import usePrevious from './usePrevious';
Expand All @@ -59,6 +61,7 @@ import useSet from './useSet';
import useSetState from './useSetState';
import useSize from './useSize';
import useTextSelection from './useTextSelection';
import useTheme from './useTheme';
import useThrottle from './useThrottle';
import useThrottleEffect from './useThrottleEffect';
import useThrottleFn from './useThrottleFn';
Expand All @@ -74,88 +77,87 @@ import useUpdateLayoutEffect from './useUpdateLayoutEffect';
import useVirtualList from './useVirtualList';
import useWebSocket from './useWebSocket';
import useWhyDidYouUpdate from './useWhyDidYouUpdate';
import useMutationObserver from './useMutationObserver';
import useTheme from './useTheme';

export {
useRequest,
useControllableValue,
useDynamicList,
useVirtualList,
useResponsive,
useEventEmitter,
useLocalStorageState,
useSessionStorageState,
useSize,
clearCache,
configResponsive,
useUpdateEffect,
useUpdateLayoutEffect,
createUpdateEffect,
useAntdTable,
useAsyncEffect,
useBoolean,
useToggle,
useDocumentVisibility,
useSelections,
useThrottle,
useThrottleFn,
useThrottleEffect,
useDebounce,
useDebounceFn,
useDebounceEffect,
usePrevious,
useMouse,
useScroll,
useBroadcastChannel,
useClickAway,
useFullscreen,
useInViewport,
useKeyPress,
useEventListener,
useHover,
useUnmount,
useSet,
useMemoizedFn,
useMap,
useControllableValue,
useCookieState,
useCountDown,
useCounter,
useCreation,
useDebounce,
useDebounceEffect,
useDebounceFn,
useDeepCompareEffect,
useDeepCompareLayoutEffect,
useDocumentVisibility,
useDrag,
useDrop,
useMount,
useCounter,
useUpdate,
useTextSelection,
useDynamicList,
useEventEmitter,
useEventListener,
useEventTarget,
useExternal,
useFavicon,
useFocusWithin,
useFullscreen,
useFusionTable,
useGetState,
useHistoryTravel,
useCookieState,
useSetState,
useHover,
useInfiniteScroll,
useInterval,
useWhyDidYouUpdate,
useTitle,
useNetwork,
useTimeout,
useReactive,
useFavicon,
useCountDown,
useWebSocket,
useLockFn,
useUnmountedRef,
useExternal,
useSafeState,
useLatest,
useInViewport,
useIsomorphicLayoutEffect,
useDeepCompareEffect,
useDeepCompareLayoutEffect,
useAsyncEffect,
useKeyPress,
useLatest,
useLocalStorageState,
useLockFn,
useLongPress,
useRafState,
useTrackedEffect,
useMap,
useMemoizedFn,
useMount,
useMouse,
useMutationObserver,
useNetwork,
usePagination,
useAntdTable,
useFusionTable,
useInfiniteScroll,
useGetState,
clearCache,
useFocusWithin,
createUpdateEffect,
usePrevious,
useRafInterval,
useRafState,
useRafTimeout,
useReactive,
useRequest,
useResetState,
useMutationObserver,
useResponsive,
useSafeState,
useScroll,
useSelections,
useSessionStorageState,
useSet,
useSetState,
useSize,
useTextSelection,
useTheme,
useThrottle,
useThrottleEffect,
useThrottleFn,
useTimeout,
useTitle,
useToggle,
useTrackedEffect,
useUnmount,
useUnmountedRef,
useUpdate,
useUpdateEffect,
useUpdateLayoutEffect,
useVirtualList,
useWebSocket,
useWhyDidYouUpdate,
};
51 changes: 51 additions & 0 deletions packages/hooks/src/useBroadcastChannel/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { act, renderHook } from '@testing-library/react';
import useBroadcastChannel from '../index';

// Skip tests if BroadcastChannel is not available (i.e., not running in a browser)
const describeOrSkip =
typeof BroadcastChannel === 'undefined' ? describe.skip : describe;

describeOrSkip('useBroadcastChannel', () => {
it('should send and receive messages', () => {
const channelName = 'test-channel';
let received: string | null = null;
const { result: sender } = renderHook(() =>
useBroadcastChannel<string>(channelName)
);
const { result: receiver } = renderHook(() =>
useBroadcastChannel<string>(channelName, {
onMessage: msg => {
received = msg;
},
})
);

act(() => {
sender.current.sendMessage('hello');
});

expect(received).toBe('hello');
});

it('should support multiple messages', () => {
const channelName = 'multi-message-channel';
const messages: string[] = [];
const { result: sender } = renderHook(() =>
useBroadcastChannel<string>(channelName)
);
renderHook(() =>
useBroadcastChannel<string>(channelName, {
onMessage: msg => {
messages.push(msg);
},
})
);

act(() => {
sender.current.sendMessage('foo');
sender.current.sendMessage('bar');
});

expect(messages).toEqual(['foo', 'bar']);
});
});
50 changes: 50 additions & 0 deletions packages/hooks/src/useBroadcastChannel/demo/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import useLocalStorageState from '../../useLocalStorageState';
import useBroadcastChannel from '../index';

interface IAuthMessage {
type: 'LOGOUT';
timestamp: number;
}

const App = () => {
const [state, setState] = useLocalStorageState('auth_token', {
defaultValue: '',
});
const authChannel = useBroadcastChannel<IAuthMessage>('auth_channel', {
onMessage: message => {
if (message.type === 'LOGOUT') {
console.log(
'Received logout message at:',
new Date(message.timestamp).toLocaleTimeString()
);
setState('');
}
},
onMessageError: event => {
console.error('Message error', event.data);
},
});

const logoutWithBroadcast = () => {
authChannel.sendMessage({
type: 'LOGOUT',
timestamp: Date.now(),
});
setState('');
};

return (
<main className="p-4 space-y-4">
<button
className="px-4 py-2 bg-yellow-600 text-white rounded hover:bg-yellow-700 transition"
onClick={logoutWithBroadcast}
>
Broadcast Logout
</button>

<p className="text-gray-700">Current Auth Token: {state}</p>
</main>
);
};

export default App;
38 changes: 38 additions & 0 deletions packages/hooks/src/useBroadcastChannel/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
nav:
path: /hooks
---

# useBroadcastChannel

A hook for communicating between browser tabs or windows using the [BroadcastChannel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API).

## Examples

### Basic usage

<code src="./demo/demo1.tsx" />

## API

```typescript
const [state, {
postMessage,
close,
}] = useBroadcastChannel<T>(channelName, options?);
```

### Result

| Property | Description | Type |
| ----------- | ---------------------------------------- | ------------------ |
| state | Last message received from the channel | `T` |
| postMessage | Send a message to the channel | `(msg: T) => void` |
| close | Close the channel and clean up listeners | `() => void` |

### Params

| Property | Description | Type | Default |
| ----------- | ---------------------------------------- | -------- | ------- |
| channelName | Name of the broadcast channel | `string` | - |
| options | Optional configuration (e.g., onMessage) | `object` | - |
26 changes: 26 additions & 0 deletions packages/hooks/src/useBroadcastChannel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useRef } from 'react';

interface BroadcastChannelOptions<T> {
onMessage?: (message: T) => void;
onMessageError?: (event: MessageEvent) => void;
}

export default function useBroadcastChannel<T>(
channelName: string,
options: BroadcastChannelOptions<T> = {}
) {
const channel = new BroadcastChannel(channelName);
const channelRef = useRef(channel);
const currentChannel = channelRef.current;

const sendMessage = (data: T) => currentChannel.postMessage(data);

useEffect(() => {
currentChannel.onmessage = event => options.onMessage?.(event.data);
currentChannel.onmessageerror = event => options.onMessageError?.(event);
}, [channelName, currentChannel, options]);

return {
sendMessage,
};
}
38 changes: 38 additions & 0 deletions packages/hooks/src/useBroadcastChannel/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
nav:
path: /hooks
---

# useBroadcastChannel

用于在浏览器标签页或窗口之间通信的 Hook,基于 [BroadcastChannel API](https://developer.mozilla.org/zh-CN/docs/Web/API/Broadcast_Channel_API)。

## 代码演示

### 基础用法

<code src="./demo/demo1.tsx" />

## API

```typescript
const [state, {
postMessage,
close,
}] = useBroadcastChannel<T>(channelName, options?);
```

### Result

| 参数 | 说明 | 类型 |
| ----------- | -------------------- | ------------------ |
| state | 最近一次收到的消息 | `T` |
| postMessage | 发送消息到频道 | `(msg: T) => void` |
| close | 关闭频道并清理监听器 | `() => void` |

### Params

| 参数 | 说明 | 类型 | 默认值 |
| ----------- | ----------------------------- | -------- | ------ |
| channelName | 频道名称 | `string` | - |
| options | 可选配置(如 onMessage 回调) | `object` | - |