diff --git a/sample-app/src/App.tsx b/sample-app/src/App.tsx index ffdabfc..0080d73 100644 --- a/sample-app/src/App.tsx +++ b/sample-app/src/App.tsx @@ -11,12 +11,16 @@ import './App.css'; function App() { const [messages, updateMessages] = useState([]); - const { channel, ably } = useChannel('your-channel-name', (message) => { - updateMessages((prev) => [...prev, message]); - }); + const [skip, setSkip] = useState(false); + const { channel, ably } = useChannel( + { channelName: 'your-channel-name', skip }, + (message) => { + updateMessages((prev) => [...prev, message]); + } + ); const { presenceData, updateStatus } = usePresence( - 'your-channel-name', + { channelName: 'your-channel-name', skip }, { foo: 'bar' }, (update) => { console.log(update); @@ -38,7 +42,7 @@ function App() { undefined | Types.ErrorInfo >(); - useChannelStateListener('your-channel-name', 'detached', (stateChange) => { + useChannelStateListener('your-channel-name', (stateChange) => { setAblyErr(JSON.stringify(stateChange.reason)); setChannelState(stateChange.current); setPreviousChannelState(stateChange.previous); @@ -80,6 +84,10 @@ function App() {
+

Skip

+

Channel detach

diff --git a/src/AblyReactHooks.ts b/src/AblyReactHooks.ts index a337f6d..a2ccd9c 100644 --- a/src/AblyReactHooks.ts +++ b/src/AblyReactHooks.ts @@ -5,6 +5,8 @@ export type ChannelNameAndOptions = { options?: Types.ChannelOptions; id?: string; subscribeOnly?: boolean; + skip?: boolean; + onConnectionError?: (error: Types.ErrorInfo) => unknown; onChannelError?: (error: Types.ErrorInfo) => unknown; }; diff --git a/src/fakes/ably.ts b/src/fakes/ably.ts index 1fd7914..a0fc49d 100644 --- a/src/fakes/ably.ts +++ b/src/fakes/ably.ts @@ -384,7 +384,7 @@ export class ChannelPresence { public unsubscribe(clientId: string, key: string) { const subsForClient = this.subscriptionsPerClient.get(clientId); - subsForClient.set(key, []); + subsForClient?.set(key, []); } private triggerSubs(subType: string, data: any) { diff --git a/src/hooks/useChannel.test.tsx b/src/hooks/useChannel.test.tsx index a9904e7..8d8252d 100644 --- a/src/hooks/useChannel.test.tsx +++ b/src/hooks/useChannel.test.tsx @@ -46,7 +46,9 @@ describe('useChannel', () => { ); await act(async () => { - otherClient.channels.get('blah').publish({ text: 'message text' }); + await otherClient.channels + .get('blah') + .publish({ text: 'message text' }); }); const messageUl = screen.getAllByRole('messages')[0]; @@ -61,8 +63,12 @@ describe('useChannel', () => { ); await act(async () => { - otherClient.channels.get('blah').publish({ text: 'message text1' }); - otherClient.channels.get('blah').publish({ text: 'message text2' }); + await otherClient.channels + .get('blah') + .publish({ text: 'message text1' }); + await otherClient.channels + .get('blah') + .publish({ text: 'message text2' }); }); const messageUl = screen.getAllByRole('messages')[0]; @@ -82,8 +88,12 @@ describe('useChannel', () => { ); await act(async () => { - ablyClient.channels.get('blah').publish({ text: 'message text1' }); - otherClient.channels.get('bleh').publish({ text: 'message text2' }); + await ablyClient.channels + .get('blah') + .publish({ text: 'message text1' }); + await otherClient.channels + .get('bleh') + .publish({ text: 'message text2' }); }); const messageUl = screen.getAllByRole('messages')[0]; @@ -143,6 +153,22 @@ describe('useChannel', () => { expect(onConnectionError).toHaveBeenCalledWith(reason); }); + it('skip param', async () => { + renderInCtxProvider( + ablyClient, + + ); + + await act(async () => { + await otherClient.channels + .get('blah') + .publish({ text: 'message text' }); + }); + + const messageUl = screen.getAllByRole('messages')[0]; + expect(messageUl.childElementCount).toBe(0); + }); + it('should use the latest version of the message callback', async () => { let callbackCount = 0; @@ -220,9 +246,9 @@ const UseChannelComponentMultipleClients = () => { return
    {messagePreviews}
; }; -const UseChannelComponent = () => { +const UseChannelComponent = ({ skip }: { skip?: boolean }) => { const [messages, updateMessages] = useState([]); - useChannel('blah', (message) => { + useChannel({ channelName: 'blah', skip }, (message) => { updateMessages((prev) => [...prev, message]); }); diff --git a/src/hooks/useChannel.ts b/src/hooks/useChannel.ts index 9fa1b53..a11f10e 100644 --- a/src/hooks/useChannel.ts +++ b/src/hooks/useChannel.ts @@ -37,7 +37,7 @@ export function useChannel( const ably = useAbly(channelHookOptions.id); - const { channelName, options: channelOptions } = channelHookOptions; + const { channelName, options: channelOptions, skip } = channelHookOptions; const channelEvent = typeof eventOrCallback === 'string' ? eventOrCallback : null; @@ -75,11 +75,14 @@ export function useChannel( const subscribeArgs: SubscribeArgs = channelEvent === null ? [listener] : [channelEvent, listener]; - handleChannelMount(channel, ...subscribeArgs); + if (!skip) { + handleChannelMount(channel, ...subscribeArgs); + } + return () => { - handleChannelUnmount(channel, ...subscribeArgs); + !skip && handleChannelUnmount(channel, ...subscribeArgs); }; - }, [channelEvent, channel]); + }, [channelEvent, channel, skip]); return { channel, ably, connectionError, channelError }; } diff --git a/src/hooks/usePresence.test.tsx b/src/hooks/usePresence.test.tsx index 9ca8243..eaa971d 100644 --- a/src/hooks/usePresence.test.tsx +++ b/src/hooks/usePresence.test.tsx @@ -105,6 +105,20 @@ describe('usePresence', () => { expect(values).toContain(`"data":{"foo":"bar"}`); }); + it('skip param', async () => { + renderInCtxProvider( + ablyClient, + + ); + + await act(async () => { + await wait(2); + }); + + const values = screen.getByRole('presence').innerHTML; + expect(values).to.not.contain(`"bar"`); + }); + it('usePresence works with multiple clients', async () => { renderInCtxProvider( ablyClient, @@ -180,8 +194,11 @@ describe('usePresence', () => { }); }); -const UsePresenceComponent = () => { - const { presenceData, updateStatus } = usePresence(testChannelName, 'bar'); +const UsePresenceComponent = ({ skip }: { skip?: boolean }) => { + const { presenceData, updateStatus } = usePresence( + { channelName: testChannelName, skip }, + 'bar' + ); const presentUsers = presenceData.map((presence, index) => { return ( diff --git a/src/hooks/usePresence.ts b/src/hooks/usePresence.ts index 0b34856..f6d0ae1 100644 --- a/src/hooks/usePresence.ts +++ b/src/hooks/usePresence.ts @@ -36,6 +36,7 @@ export function usePresence( : params.subscribeOnly; const channel = ably.channels.get(params.channelName, params.options); + const skip = params.skip; const { connectionError, channelError } = useStateErrors(params); @@ -75,14 +76,14 @@ export function usePresence( }; const useEffectHook = () => { - onMount(); + !skip && onMount(); return () => { onUnmount(); }; }; // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(useEffectHook, []); + useEffect(useEffectHook, [skip]); const updateStatus = useCallback( (messageOrPresenceObject: T) => {