-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ECO-5184] feat: update channel hooks implementation
Because of implicit `attach()` hooks can produce additional errors e.g. when updating ably client to avoid this situation, channels created inside `ChannelProvider` now use `attachOnSubscribe = false` flag and attach is happening explicitly.
- Loading branch information
Showing
12 changed files
with
158 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import type * as Ably from 'ably'; | ||
|
||
export const INACTIVE_CONNECTION_STATES: Ably.ConnectionState[] = ['suspended', 'closing', 'closed', 'failed']; | ||
export const INACTIVE_CHANNEL_STATES: Ably.ChannelState[] = ['failed', 'suspended', 'detaching']; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
src/platform/react-hooks/src/hooks/useChannelAttach.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { renderHook } from '@testing-library/react'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { useChannelAttach } from './useChannelAttach.js'; | ||
|
||
interface LocalTestContext { | ||
useChannelAttach: typeof useChannelAttach; | ||
} | ||
|
||
describe('useChannelAttach', () => { | ||
const fakeAblyClientRef: any = {}; | ||
|
||
beforeEach<LocalTestContext>(async (context) => { | ||
vi.doMock('./useConnectionStateListener.js', () => ({ | ||
useConnectionStateListener: vi.fn(), | ||
})); | ||
|
||
vi.doMock('./useAbly.js', () => ({ | ||
useAbly: () => fakeAblyClientRef.current, | ||
})); | ||
|
||
context.useChannelAttach = (await import('./useChannelAttach.js')).useChannelAttach; | ||
fakeAblyClientRef.current = { connection: { state: 'initialized' } }; | ||
}); | ||
|
||
it<LocalTestContext>('should call attach on render', ({ useChannelAttach }) => { | ||
const channel = { attach: vi.fn(() => Promise.resolve()) }; | ||
const { result } = renderHook(() => useChannelAttach(channel, undefined, false)); | ||
|
||
expect(result.current.connectionState).toBe('initialized'); | ||
expect(channel.attach).toHaveBeenCalled(); | ||
}); | ||
|
||
it<LocalTestContext>('should not call attach when skipped', ({ useChannelAttach }) => { | ||
const channel = { attach: vi.fn(() => Promise.resolve()) }; | ||
const { result } = renderHook(() => useChannelAttach(channel, undefined, true)); | ||
|
||
expect(result.current.connectionState).toBe('initialized'); | ||
expect(channel.attach).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it<LocalTestContext>('should not call attach when in failed state', ({ useChannelAttach }) => { | ||
fakeAblyClientRef.current = { connection: { state: 'failed' } }; | ||
const channel = { attach: vi.fn(() => Promise.resolve()) }; | ||
const { result } = renderHook(() => useChannelAttach(channel, undefined, false)); | ||
|
||
expect(result.current.connectionState).toBe('failed'); | ||
expect(channel.attach).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it<LocalTestContext>('should call attach when go back to the connected state', async ({ useChannelAttach }) => { | ||
fakeAblyClientRef.current = { connection: { state: 'suspended' } }; | ||
const channel = { attach: vi.fn(() => Promise.resolve()) }; | ||
const { result, rerender } = renderHook(() => useChannelAttach(channel, undefined, false)); | ||
|
||
expect(result.current.connectionState).toBe('suspended'); | ||
expect(channel.attach).not.toHaveBeenCalled(); | ||
|
||
fakeAblyClientRef.current = { connection: { state: 'connected' } }; | ||
rerender(); | ||
|
||
expect(result.current.connectionState).toBe('connected'); | ||
expect(channel.attach).toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type * as Ably from 'ably'; | ||
import { useEffect, useState } from 'react'; | ||
import { useConnectionStateListener } from './useConnectionStateListener.js'; | ||
import { useAbly } from './useAbly.js'; | ||
import { INACTIVE_CONNECTION_STATES } from './constants.js'; | ||
|
||
interface ChannelAttachResult { | ||
connectionState: Ably.ConnectionState; | ||
} | ||
|
||
export function useChannelAttach( | ||
channel: Ably.RealtimeChannel, | ||
ablyId: string | undefined, | ||
skip: boolean, | ||
): ChannelAttachResult { | ||
const ably = useAbly(ablyId); | ||
|
||
// we need to listen for the current connection state in order to react to it. | ||
// for example, we should attach when first connected, re-enter when reconnected, | ||
// and be able to prevent attaching when the connection is in an inactive state. | ||
// all of that can be achieved by using the useConnectionStateListener hook. | ||
const [connectionState, setConnectionState] = useState(ably.connection.state); | ||
useConnectionStateListener((stateChange) => { | ||
setConnectionState(stateChange.current); | ||
}, ablyId); | ||
|
||
if (ably.connection.state !== connectionState) { | ||
setConnectionState(ably.connection.state); | ||
} | ||
|
||
const shouldAttachToTheChannel = !skip && !INACTIVE_CONNECTION_STATES.includes(connectionState); | ||
|
||
useEffect(() => { | ||
if (shouldAttachToTheChannel) { | ||
channel.attach().catch((reason) => { | ||
// we use a fire-and-forget approach for attaching, but calling detach during the attaching process or while | ||
// suspending can cause errors that will be automatically resolved | ||
console.log(reason); | ||
}); | ||
} | ||
}, [shouldAttachToTheChannel, channel]); | ||
|
||
// we expose `connectionState` here for reuse in the usePresence hook, where we need to prevent | ||
// entering and leaving presence in a similar manner | ||
return { connectionState }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters