Skip to content
This repository was archived by the owner on Mar 24, 2023. It is now read-only.

fix: replace useSubscription hook with createSubscription factory #55

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
35 changes: 17 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,28 +111,27 @@ export default function Status() {

**Example Subscribing and Receiving messages**

```js
```jsx
import React from 'react';

import { useSubscription } from 'mqtt-react-hooks';

export default function Status() {
/* Message structure:
* topic: string
* message: string
*/
const { message } = useSubscription([
'room/esp32/testing',
'room/esp32/light',
]);

return (
<>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span>{`topic:${message.topic} - message: ${message.message}`}</span>
</div>
</>
);
export default function Subscriber() {
const { Subscription: Esp32Subscription, Esp32MessageContext } = createSubscription()

return (
<Esp32Subscription
topic={['room/esp32/testing','room/esp32/light']}
subscribeCallback={message => console.log(`subscribed to ${JSON.stringify(message?.topic)}`)}
messageCallback={message => console.log(`received ${JSON.stringify(message)}`)}>
<Esp32MessageContext.Consumer>
{message =>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span>{`topic:${message.topic} - message: ${message.message}`}</span>
</div>
}
</Esp32MessageContext.Consumer>
</Esp32Subscription>);
}
```

Expand Down
8 changes: 7 additions & 1 deletion lib/Connector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useEffect, useState, useMemo, useRef } from 'react';
import React, {
useEffect,
useState,
useMemo,
useRef,
useCallback,
} from 'react';

import { connect, MqttClient } from 'mqtt';

Expand Down
75 changes: 75 additions & 0 deletions lib/Subscription.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useEffect, useState, useCallback, createContext } from 'react';

import { IClientSubscribeOptions } from 'mqtt';
import { matches } from 'mqtt-pattern';

import { IMessage, SubscriptionProps } from './types';
import useMqttState from './useMqttState';

// The message receiver has to be in a different component from the component that holds
// the subscription state, so that message receipt doesn't trigger subscription reset
const createMessageReceiver = MessageContext =>
function ({ topic, children, callback }) {
const [message, setMessage] = useState<IMessage | undefined>(undefined);
const { client, parserMethod } = useMqttState();

const receiveMessage = useCallback(
(receivedTopic: string, receivedMessage: any) => {
if ([topic].flat().some(rTopic => matches(rTopic, receivedTopic))) {
setMessage({
topic: receivedTopic,
message:
parserMethod?.(receivedMessage) || receivedMessage.toString(),
});
callback?.(receivedTopic, receivedMessage);
}
},
[parserMethod, topic],
);

useEffect(() => {
client?.on('message', receiveMessage);
return () => {
client?.off('message', receiveMessage);
};
}, [client]);

return (
<MessageContext.Provider value={message}>
{children}
</MessageContext.Provider>
);
};

function createSubscriptionComponent(MessageReceiver) {
return function ({
topic,
options = {} as IClientSubscribeOptions,
subscribeCallback,
messageCallback,
children,
}: SubscriptionProps) {
const { client } = useMqttState();
useEffect(() => {
client?.subscribe(topic, options, subscribeCallback);
return () => {
client?.unsubscribe(topic, options);
};
}, [client, topic, options]);
return (
<MessageReceiver topic={topic} callback={messageCallback}>
{children}
</MessageReceiver>
);
};
}

export default function createSubscription() {
const MessageContext = createContext({} as IMessage);
const MessageReceiver = createMessageReceiver(MessageContext);
const Subscription = createSubscriptionComponent(MessageReceiver);
return {
Subscription,
MessageContext,
};
}
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as useSubscription } from './useSubscription';
export { default as createSubscription } from './Subscription';
export { default as useMqttState } from './useMqttState';

export { default as Connector } from './Connector';
Expand Down
19 changes: 18 additions & 1 deletion lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { MqttClient, IClientOptions } from 'mqtt';
import React from 'react';

import {
MqttClient,
IClientOptions,
IClientSubscribeOptions,
ClientSubscribeCallback,
OnMessageCallback,
} from 'mqtt';

export interface Error {
name: string;
Expand All @@ -13,6 +21,15 @@ export interface ConnectorProps {
children: React.ReactNode;
}

export interface SubscriptionProps {
topic: string | string[];
client?: MqttClient | null;
options: IClientSubscribeOptions;
subscribeCallback?: ClientSubscribeCallback;
messageCallback?: OnMessageCallback;
children: React.ReactNode | React.ReactNode[];
}

export interface IMqttContext {
connectionStatus: string | Error;
client?: MqttClient | null;
Expand Down
53 changes: 0 additions & 53 deletions lib/useSubscription.tsx

This file was deleted.