fix: prevent Room re-renders from throwing an error

This commit is contained in:
Jeremy Kahn 2022-08-15 21:38:56 -05:00
parent 7948b51cc8
commit b9e9ae398e
4 changed files with 64 additions and 17 deletions

View File

@ -1,25 +1,25 @@
import { useMemo } from 'react'
import { useState } from 'react'
import { useParams } from 'react-router-dom'
import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography'
import { usePeerRoom } from '../../hooks/usePeerRoom'
import { PeerRoom } from '../../services/PeerRoom'
enum PeerActions {
MESSAGE = 'MESSAGE',
}
export function Room() {
const { roomId = '' } = useParams()
interface RoomProps {
peerRoom: PeerRoom
roomId: string
}
const { makeAction } = usePeerRoom({
appId: `${window.location.origin}_${process.env.REACT_APP_NAME}`,
roomId,
})
function Room({ peerRoom, roomId }: RoomProps) {
const { makeAction } = peerRoom
const [sendMessage, receiveMessage] = useMemo(
() => makeAction<string>(PeerActions.MESSAGE),
[makeAction]
const [[sendMessage, receiveMessage]] = useState(() =>
makeAction<string>(PeerActions.MESSAGE)
)
receiveMessage(message => {
@ -43,3 +43,20 @@ export function Room() {
</div>
)
}
function RoomLoader() {
const { roomId = '' } = useParams()
const peerRoom = usePeerRoom({
appId: `${process.env.REACT_APP_NAME}`,
roomId,
})
if (peerRoom) {
return <Room peerRoom={peerRoom} roomId={roomId} />
} else {
return <>Loading...</>
}
}
export { RoomLoader as Room }

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo } from 'react'
import { useEffect, useState } from 'react'
import { PeerRoom } from '../../services/PeerRoom'
import { PeerRoom, getPeerRoom } from '../../services/PeerRoom'
interface PeerRoomProps {
appId: string
@ -8,17 +8,19 @@ interface PeerRoomProps {
}
export function usePeerRoom({ appId, roomId }: PeerRoomProps) {
const peerRoom = useMemo(() => {
const peerRoom = new PeerRoom({ appId }, roomId)
const [peerRoom, setPeerRoom] = useState<PeerRoom | null>(null)
return peerRoom
useEffect(() => {
;(async () => {
setPeerRoom(await getPeerRoom({ appId }, roomId))
})()
}, [appId, roomId])
useEffect(() => {
return () => {
peerRoom.leaveRoom()
peerRoom?.leaveRoom()
}
}, [appId, peerRoom, roomId])
}, [peerRoom])
return peerRoom
}

View File

@ -1,5 +1,7 @@
import { joinRoom, Room, RoomConfig } from 'trystero'
import { sleep } from '../../utils'
export class PeerRoom {
private room: Room
@ -20,3 +22,25 @@ export class PeerRoom {
return this.room.makeAction<T>(namespace)
}
}
// This abstraction is necessary because it takes some time for a PeerRoom to
// be torn down, and there is no way to detect when that happens. If a new
// PeerRoom is instantiated with the same config and roomId before the previous
// one is torn down, an error is thrown. The workaround is to continually
// trying to instantiate a PeerRoom until it succeeds.
export const getPeerRoom = async (config: RoomConfig, roomId: string) => {
const timeout = 1000
const epoch = Date.now()
do {
if (Date.now() - epoch > timeout) {
throw new Error('Could not create PeerRoom')
}
try {
return new PeerRoom(config, roomId)
} catch (e) {}
await sleep(100)
} while (true)
}

4
src/utils.ts Normal file
View File

@ -0,0 +1,4 @@
export const sleep = (milliseconds: number): Promise<void> =>
new Promise<void>(res => {
setTimeout(res, milliseconds)
})