forked from Shiloh/remnantchat
fix: prevent Room re-renders from throwing an error
This commit is contained in:
parent
7948b51cc8
commit
b9e9ae398e
@ -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 }
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
4
src/utils.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const sleep = (milliseconds: number): Promise<void> =>
|
||||
new Promise<void>(res => {
|
||||
setTimeout(res, milliseconds)
|
||||
})
|
Loading…
Reference in New Issue
Block a user