Skip to content

Commit

Permalink
feat(hook): implement hook logic
Browse files Browse the repository at this point in the history
  • Loading branch information
unadlib committed Mar 31, 2024
1 parent 4450e25 commit fad6307
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 14 deletions.
63 changes: 60 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 <button onClick={handleClick}>Ping</button>;
};
```

#### 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

Expand Down
14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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"
}
}
70 changes: 69 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -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<T extends BaseInteraction = any> = ITransport<T> & {
listen: <K extends keyof T['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 = <T extends keyof typeof TransportMap>(
name: T,
options: TransportOptionsMap[T]
) => {
const transportRef = useRef<ITransport | null>(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;
};
101 changes: 99 additions & 2 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -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<number>;
};
emit: {
bar: (value: number) => Promise<void>;
};
};
const transport0: ITransport<Reverse<Interaction>> = 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<Interaction> = 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);
});
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down

0 comments on commit fad6307

Please sign in to comment.