@harelpls/use-pusher
Version:
A wrapper around pusher-js for easy-as hooks in React.
279 lines (203 loc) • 7.85 kB
Markdown
# `/use-pusher`
> Easy as [React hooks](https://reactjs.org/docs/hooks-intro.html) that integrate with the [`pusher-js`](https://github.com/pusher/pusher-js) library.
[](https://www.npmjs.com/package/@harelpls/use-pusher) 
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Install](#install)
- [Hooks](#hooks)
- [Usage](#usage)
- [`useChannel`](#usechannel)
- [`usePresenceChannel`](#usepresencechannel)
- [`useEvent`](#useevent)
- [`useTrigger`](#usetrigger)
- [`usePusher`](#usepusher)
- [Trigger Server](#trigger-server)
- [`useClientTrigger`](#useclienttrigger)
- [Typescript](#typescript)
- [Testing](#testing)
- [React Native](#react-native)
- [Contributing](#contributing)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## [API Reference/Docs](https://use-pusher-docs.netlify.com/)
## Install
```bash
yarn add /use-pusher
```
## Hooks
- [`useChannel`](#usechannel)
- [`usePresenceChannel`](#usepresencechannel)
- [`useEvent`](#useevent)
- [`useTrigger`](#usetrigger)
- [`useClientTrigger`](#useclienttrigger)
- [`usePusher`](#usepusher)
## Usage
You must wrap your app with a `PusherProvider` and pass it config props for [`pusher-js`](https://github.com/pusher/pusher-js) initialisation.
```typescript
import React from "react";
import { PusherProvider } from "@harelpls/use-pusher";
const config = {
// required config props
clientKey: "client-key",
cluster: "ap4",
// optional if you'd like to trigger events. BYO endpoint.
// see "Trigger Server" below for more info
triggerEndpoint: "/pusher/trigger",
// required for private/presence channels
// also sends auth headers to trigger endpoint
authEndpoint: "/pusher/auth",
auth: {
headers: { Authorization: "Bearer token" },
},
};
// Wrap app in provider
const App = () => {
<PusherProvider {...config}>
<Example />
</PusherProvider>;
};
```
## `useChannel`
Use this hook to subscribe to a channel.
```typescript
// returns channel instance.
const channel = useChannel("channel-name");
```
## `usePresenceChannel`
Augments a regular channel with member functionality.
```typescript
const Example = () => {
const { members, myID } = usePresenceChannel('presence-awesome');
return (
<ul>
{Object.entries(members)
// filter self from members
.filter(([id]) => id !== myID)
// map them to a list
.map(([id, info]) => (
<li key={id}>{info.name}</li>
))
}
</ul>
)
}
```
## `useEvent`
Bind to events on a channel with a callback.
```typescript
const Example = () => {
const [message, setMessages] = useState();
const channel = useChannel("channel-name");
useEvent(channel, "message", ({ data }) =>
setMessages((messages) => [...messages, data])
);
};
```
_Note_: This will bind and unbind to the event on each render. You may want to memoise your callback with `useCallback` before passing it in if you notice performance issues.
## `useTrigger`
A helper function to create a **server triggered** event. BYO server (See [Trigger Server](#trigger-server) below). Pass in `triggerEndpoint` prop to `<PusherProvider />`. Any auth headers from `config.auth.headers` automatically get passed to the `fetch` call.
```typescript
import { useTrigger } from '/use-pusher';
const Example = () => {.
const trigger = useTrigger("channel-name");
const handleClick = () =>
trigger("event-name", "hello");
return (
<button onClick={handleClick}>Say Hello</button>
)
}
```
## `usePusher`
Get access to the Pusher instance to do other things.
```typescript
import { usePusher } from "@harelpls/use-pusher";
const Example = () => {
const { client } = usePusher();
client.log("Look ma, logs!");
return null;
};
```
## Trigger Server
In order to trigger an event, you'll have to create a simple lambda (or an express server if that's your thing). Below is a short lambda that can handle triggered events from `useTrigger`.
```typescript
import Pusher from "pusher";
const pusher = new Pusher({
appId: "app-id",
key: "client-key",
secret: "mad-secret",
cluster: "ap4",
});
export async function handler(event) {
const { channelName, eventName, data } = JSON.parse(event.body);
pusher.trigger(channelName, eventName, data);
return { statusCode: 200 };
}
```
## `useClientTrigger`
> _I don't want a server though_
I hear ya. If you're feeling audacious, you can use [client events](https://pusher.com/docs/channels/using_channels/events#triggering-client-events) to push directly from the client:
```typescript
import { useChannel, useClientTrigger } from "@harelpls/use-pusher";
const Example = () => {
const channel = useChannel("presence-ca");
const trigger = useClientTrigger(channel);
const handleClientEvent = () => {
trigger("Pew pew");
};
return <button onClick={handleClientEvent}>Fire</button>;
};
```
## Typescript
This project was built using typescript, so types are built-in. Yeeeew!
## Testing
I've teamed up with [](https://github.com/nikolalsvk) on [`pusher-js-mock`](https://github.com/nikolalsvk/pusher-js-mock) to bring y'all a great pusher mock.
Testing emitted events with jest can be achieved using `jest.mock` and `-library/react` (or `enzyme`, though your tests should reflect what the user should see **NOT** how the component handles events internally):
```typescript
// Example.tsx
import React from "react";
import { useChannel, useEvent } from "@harelpls/use-pusher";
const Example = () => {
const [title, setTitle] = useState();
const channel = useChannel("my-channel");
useEvent(channel, "title", ({ data }) => setTitle(data));
return <span>{title}</span>;
};
// Example.test.tsx
import { render, act } from "@testing-library/react";
import { PusherMock, PusherChannelMock } from "pusher-js-mock";
// mock out the result of the useChannel hook
const mockChannel = new PusherChannelMock();
jest.mock("@harelpls/use-pusher", () => ({
...require.requireActual("@harelpls/use-pusher"),
useChannel: () => mockChannel,
}));
test("should show a title when it receives a title event", async () => {
// mock the client
const client = new PusherMock("client-key", { cluster: "ap4" });
// render component and provider with a mocked context value
const { findByText } = render(
<PusherProvider clientKey="client-key" cluster="ap4" value={{ client }}>
<Example />
</PusherProvider>
);
// emit an event on the mocked channel
act(() => mockChannel.emit("title", { data: "Hello world" }));
// assert expectations
expect(await findByText("Hello world")).toBeInTheDocument();
});
```
[Check out the example tests](https://github.com/mayteio/use-pusher/blob/master/src/__tests__/Example.tsx) for testing presence channels.
## React Native
This package comes with React Native support. Import your `PusherProvider` from `/use-pusher/react-native` instead of the default `/use-pusher`. All exports are re-exported from there.
Ensure you add the netinfo package to your project too: [`-native-community/netinfo`](https://github.com/react-native-community/react-native-netinfo).
```ts
import { PusherProvider, useChannel } from "@harelpls/use-pusher/react-native";
```
## Contributing
1. Clone the repository and run `yarn && yarn test:watch`
2. Get coding!
Please write tests (100% jest coverage) and types.
## License
MIT © [](https://github.com/@mayteio)
---
This hook is created using [create-react-hook](https://github.com/hermanya/create-react-hook).