diff --git a/README.md b/README.md
index ec84980..cdd07c9 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,21 @@ A React hook with simple and responsible universal transports.
### Motivation
+`use-transport` is a React hook that provides a simple and responsible way to manage data transport. It is designed to be used with [`data-transport`](https://github.com/unadlib/data-transport) to provide a universal transport solution.
+
+`data-transport` is a generic and responsible communication transporter:
+
+- iframe
+- Broadcast
+- Web Worker
+- Service Worker
+- Shared Worker
+- Browser Extension
+- Node.js
+- WebRTC
+- Electron
+- More transport port
+
### Installation
```bash
@@ -18,13 +33,55 @@ yarn add use-transport data-transport
### Features
-### Example
+- Simple and responsible
+- Universal transport
+- Support for multiple transport ports
+- Support for mock transport
+- Full TypeScript support
### API
-### Options
+You can use the `use-transport` hook to create a transport instance.
+
+```jsx
+import React from 'react';
+import { useTransport } from 'use-transport';
+
+const App = () => {
+ const transport = useTransport('IFrameMain', {});
+
+ transport.listen(
+ 'hello',
+ async () => {
+ return 'world';
+ },
+ []
+ );
+
+ const handleClick = async () => {
+ const response = await transport.emit('ping');
+ console.log(response);
+ };
+
+ return ;
+};
+```
+
+#### Parameters
+
+| Name | Type | Description |
+| --------- | ------ | ---------------------- |
+| `type` | enums | Transport port type |
+| `options` | object | Transport port options |
+
+#### Returns
+
+| Name | Type | Description |
+| ------------------ | -------- | -------------------------- |
+| `transport.emit` | function | Emit a message |
+| `transport.listen` | hook | Listen a message with deps |
-### Return
+> The `use-transport` hook returns a transport instance. more API details can be found in the [data-transport](https://github.com/unadlib/data-transport) documentation.
## License
diff --git a/package.json b/package.json
index f10ad9c..8ef20bc 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "use-transport",
- "version": "0.0.1",
+ "version": "0.1.0",
"description": "A React hook with simple and responsible universal transports",
"main": "dist/index.cjs.js",
"unpkg": "dist/index.umd.js",
@@ -33,7 +33,13 @@
"url": "https://github.com/unadlib/use-transport/issues"
},
"homepage": "https://github.com/unadlib/use-transport#readme",
- "keywords": [],
+ "keywords": [
+ "react",
+ "hook",
+ "transport",
+ "use-transport",
+ "data-transport"
+ ],
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
@@ -46,7 +52,7 @@
"@typescript-eslint/parser": "^7.4.0",
"commitizen": "^4.3.0",
"coveralls": "^3.1.1",
- "data-transport": "^4.3.5",
+ "data-transport": "^4.3.6",
"eslint": "^8.36.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.8.0",
@@ -82,7 +88,7 @@
},
"peerDependencies": {
"@types/react": "^18.0 || ^17.0",
- "data-transport": "^4.3.5",
+ "data-transport": "^4.3.6",
"react": "^18.0 || ^17.0"
}
}
diff --git a/src/index.ts b/src/index.ts
index bc81dd5..731b4d7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1 +1,69 @@
-export const add = (a: number, b: number) => a + b;
+import { useEffect, useRef } from 'react';
+import {
+ type Transport as ITransport,
+ type BaseInteraction,
+ createTransport,
+ type TransportMap,
+ type TransportOptionsMap,
+} from 'data-transport';
+
+export type Transport = ITransport & {
+ listen: (
+ /**
+ * The name of the event to listen for.
+ */
+ name: K,
+ /**
+ * The callback to invoke when the event is emitted.
+ */
+ fn: T['listen'][K],
+ /**
+ * The dependencies to watch for changes and re-invoke the callback.
+ */
+ deps?: any[]
+ ) => void;
+};
+
+/**
+ * Create a transport instance with the given name and options.
+ */
+export const useTransport = (
+ name: T,
+ options: TransportOptionsMap[T]
+) => {
+ const transportRef = useRef(null);
+ if (!transportRef.current) {
+ transportRef.current = createTransport(name, options);
+ const { listen } = transportRef.current;
+ transportRef.current.listen = ((
+ options,
+ callback: (...args: unknown[]) => unknown,
+ deps: unknown[] = []
+ ) => {
+ const listenerRef = useRef<((...args: unknown[]) => unknown) | null>(
+ null
+ );
+ useEffect(() => {
+ listenerRef.current = callback;
+ }, deps);
+ useEffect(() => {
+ const removeListener = listen.call(
+ transportRef.current,
+ options,
+ (...args: unknown[]) => listenerRef.current?.(...args)
+ );
+ return () => {
+ // Remove the listener when the component unmounts.
+ removeListener?.();
+ };
+ }, []);
+ }) as Transport['listen'];
+ }
+ useEffect(() => {
+ return () => {
+ // Dispose the transport instance when the component unmounts.
+ transportRef.current?.dispose();
+ };
+ }, []);
+ return transportRef.current as Transport;
+};
diff --git a/test/index.test.ts b/test/index.test.ts
index 6ebd531..e7e7177 100644
--- a/test/index.test.ts
+++ b/test/index.test.ts
@@ -1,3 +1,100 @@
-test('', () => {
- //
+import { act, renderHook } from '@testing-library/react';
+import { useState } from 'react';
+import {
+ mockPorts,
+ type Transport as ITransport,
+ Reverse,
+ createTransport,
+} from 'data-transport';
+
+import { useTransport, type Transport } from '../src/index';
+
+test('Base', async () => {
+ const ports = mockPorts();
+ type Interaction = {
+ listen: {
+ foo: (value: number) => Promise;
+ };
+ emit: {
+ bar: (value: number) => Promise;
+ };
+ };
+ const transport0: ITransport> = createTransport(
+ 'Base',
+ ports.create()
+ );
+ const fn0 = jest.fn();
+ transport0.listen('bar', async (value) => {
+ fn0(value);
+ });
+ const fn1 = jest.fn();
+ const { result, unmount } = renderHook(() => {
+ const [state, setState] = useState(0);
+ const transport: Transport = useTransport('Base', ports.main);
+ transport.listen(
+ 'foo',
+ async (value) => {
+ const nextState = value + state;
+ setState(nextState);
+ fn1(value);
+ return nextState;
+ },
+ [state]
+ );
+ return {
+ state,
+ setState,
+ transport,
+ };
+ });
+ expect(result.current.state).toEqual(0);
+
+ act(() => {
+ result.current.transport.emit('bar', 1);
+ });
+ expect(result.current.state).toEqual(0);
+ expect(fn0).toHaveBeenLastCalledWith(1);
+
+ let fooResult = await act(async () => {
+ return transport0.emit('foo', 1);
+ });
+ expect(result.current.state).toEqual(1);
+ expect(fn0).toHaveBeenLastCalledWith(1);
+ expect(fooResult).toEqual(1);
+
+ fooResult = await act(async () => {
+ return transport0.emit('foo', 2);
+ });
+ expect(result.current.state).toEqual(3);
+ expect(fn0).toHaveBeenLastCalledWith(1);
+ expect(fooResult).toEqual(3);
+
+ fooResult = await act(async () => {
+ return transport0.emit('foo', 3);
+ });
+ expect(result.current.state).toEqual(6);
+ expect(fn0).toHaveBeenLastCalledWith(1);
+ expect(fn0).toHaveBeenCalledTimes(1);
+ expect(fn1).toHaveBeenCalledTimes(3);
+ expect(fooResult).toEqual(6);
+
+ act(() => {
+ result.current.transport.emit('bar', 2);
+ });
+ expect(result.current.state).toEqual(6);
+ expect(fn0).toHaveBeenLastCalledWith(2);
+ expect(fn0).toHaveBeenCalledTimes(2);
+ expect(fn1).toHaveBeenCalledTimes(3);
+
+ act(() => {
+ unmount();
+ });
+
+ act(() => {
+ transport0.emit('foo', 2);
+ });
+ expect(result.current.state).toEqual(6);
+ expect(fn0).toHaveBeenLastCalledWith(2);
+ expect(fn0).toHaveBeenCalledTimes(2);
+ expect(fn1).toHaveBeenCalledTimes(3);
});
diff --git a/yarn.lock b/yarn.lock
index 8282590..2fb0e25 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1753,10 +1753,10 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
-data-transport@^4.3.5:
- version "4.3.5"
- resolved "https://registry.yarnpkg.com/data-transport/-/data-transport-4.3.5.tgz#60430fb6b5d6cb835db5f7ba3fcefdbd9bf78510"
- integrity sha512-zB76szk3yWl51eRhemhZIYd9OAHkce4wbo5jbQJAGNjWxkpcKOCoRnxCGyrjFHqqCDUs+KfFH5vQl30ZvRQbDQ==
+data-transport@^4.3.6:
+ version "4.3.6"
+ resolved "https://registry.yarnpkg.com/data-transport/-/data-transport-4.3.6.tgz#6f51f5d91a9d43e5f4ec1b491d009e80c47e243c"
+ integrity sha512-YIhrpsHU4xy4BX4RH9cPkDvpF2YmCmVnyV6yJQ79MrSg74mEdMel16tGyL/BkCSZDned0sqBye8jC5fqQxa5PQ==
dependencies:
uuid "^9.0.0"