-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
481 additions
and
275 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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,89 @@ | ||
import { Room } from '@ably/chat'; | ||
import { useEffect, useState } from 'react'; | ||
|
||
import { useLogger } from '../hooks/use-logger.js'; | ||
import { useRoomContext } from './use-room-context.js'; | ||
import { useStableReference } from './use-stable-reference.js'; | ||
|
||
/** | ||
* Given a room promise, this hook will return the room object once it has been resolved. This is useful | ||
* in hooks like useRoom to provide a direct reference to the room object, as Promises aren't usually the best | ||
* thing to be passing around React components. | ||
* | ||
* @param roomId The roomId of the room | ||
* @param room The room promise that we're waiting to resolve | ||
* @returns The room object if it has resolved, otherwise undefined | ||
*/ | ||
export const useEventualRoom = (): Room | undefined => { | ||
const [roomState, setRoomState] = useState<Room | undefined>(); | ||
const context = useRoomContext('useEventualRoom'); | ||
const logger = useLogger(); | ||
logger.trace('useEventualRoom();', { roomId: context.roomId }); | ||
|
||
useEffect(() => { | ||
logger.debug('useEventualRoom(); running useEffect', { roomId: context.roomId }); | ||
let unmounted = false; | ||
void context.room | ||
.then((room: Room) => { | ||
if (unmounted) { | ||
logger.debug('useEventualRoom(); already unmounted', { roomId: context.roomId }); | ||
return; | ||
} | ||
|
||
logger.debug('useEventualRoom(); resolved', { roomId: context.roomId }); | ||
setRoomState(room); | ||
}) | ||
.catch((error: unknown) => { | ||
logger.error('Failed to get room', { roomId: context.roomId, error }); | ||
}); | ||
|
||
return () => { | ||
logger.debug('useEventualRoom(); cleanup', { roomId: context.roomId }); | ||
unmounted = true; | ||
}; | ||
}, [context, logger]); | ||
|
||
return roomState; | ||
}; | ||
|
||
/** | ||
* Similar to useEventualRoom, but instead of providing the room itself, it provides a property of the room - e.g. | ||
* Messages. We use this to eventually provide access to underlying room interfaces as non-promise values | ||
* in hooks like useMessages. | ||
* @param roomId The roomId of the room | ||
* @param room The room promise that we're waiting to resolve | ||
* @param onResolve A function that will be called when the room promise resolves, and will return the property of the room | ||
* @returns | ||
*/ | ||
export const useEventualRoomProperty = <T>(onResolve: (room: Room) => T) => { | ||
const [roomState, setRoomState] = useState<T | undefined>(); | ||
const context = useRoomContext('useEventualRoomProperty'); | ||
const logger = useLogger(); | ||
logger.trace('useEventualRoomProperty();', { roomId: context.roomId }); | ||
const onResolveRef = useStableReference(onResolve); | ||
|
||
useEffect(() => { | ||
let unmounted = false; | ||
logger.debug('useEventualRoomProperty(); running useEffect', { roomId: context.roomId }); | ||
void context.room | ||
.then((room: Room) => { | ||
if (unmounted) { | ||
logger.debug('useEventualRoomProperty(); already unmounted', { roomId: context.roomId }); | ||
return; | ||
} | ||
|
||
logger.debug('useEventualRoomProperty(); resolved', { roomId: context.roomId }); | ||
setRoomState(onResolveRef(room)); | ||
}) | ||
.catch((error: unknown) => { | ||
logger.error('Failed to get room', { roomId: context.roomId, error }); | ||
}); | ||
|
||
return () => { | ||
logger.debug('useEventualRoomProperty(); cleanup', { roomId: context.roomId }); | ||
unmounted = true; | ||
}; | ||
}, [context, logger, onResolveRef]); | ||
|
||
return roomState; | ||
}; |
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,25 @@ | ||
import { useCallback, useEffect, useRef } from 'react'; | ||
|
||
/** | ||
* The type of a callback function that can be stored in the reference. | ||
*/ | ||
type Callback<CallbackArguments extends unknown[], ReturnType> = (...args: CallbackArguments) => ReturnType; | ||
|
||
/** | ||
* In some cases, we want to use a callback that is always the same, and always persists across renders. | ||
* This function creates a stable reference to a callback, so that it can be used in a `useEffect` or `useCallback` | ||
* without causing unnecessary re-renders. | ||
* | ||
* @param callback The callback to turn into a stable reference | ||
* @returns A stable reference to the callback | ||
*/ | ||
export const useStableReference = <Arguments extends unknown[], ReturnType>( | ||
callback: Callback<Arguments, ReturnType>, | ||
): Callback<Arguments, ReturnType> => { | ||
const ref = useRef<Callback<Arguments, ReturnType>>(callback); | ||
useEffect(() => { | ||
ref.current = callback; | ||
}); | ||
|
||
return useCallback((...args: Arguments) => ref.current(...args), []); | ||
}; |
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
Oops, something went wrong.