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 { useParams } from 'react-router-dom'
|
||||||
import Button from '@mui/material/Button'
|
import Button from '@mui/material/Button'
|
||||||
import Typography from '@mui/material/Typography'
|
import Typography from '@mui/material/Typography'
|
||||||
|
|
||||||
import { usePeerRoom } from '../../hooks/usePeerRoom'
|
import { usePeerRoom } from '../../hooks/usePeerRoom'
|
||||||
|
import { PeerRoom } from '../../services/PeerRoom'
|
||||||
|
|
||||||
enum PeerActions {
|
enum PeerActions {
|
||||||
MESSAGE = 'MESSAGE',
|
MESSAGE = 'MESSAGE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Room() {
|
interface RoomProps {
|
||||||
const { roomId = '' } = useParams()
|
peerRoom: PeerRoom
|
||||||
|
roomId: string
|
||||||
|
}
|
||||||
|
|
||||||
const { makeAction } = usePeerRoom({
|
function Room({ peerRoom, roomId }: RoomProps) {
|
||||||
appId: `${window.location.origin}_${process.env.REACT_APP_NAME}`,
|
const { makeAction } = peerRoom
|
||||||
roomId,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [sendMessage, receiveMessage] = useMemo(
|
const [[sendMessage, receiveMessage]] = useState(() =>
|
||||||
() => makeAction<string>(PeerActions.MESSAGE),
|
makeAction<string>(PeerActions.MESSAGE)
|
||||||
[makeAction]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
receiveMessage(message => {
|
receiveMessage(message => {
|
||||||
@ -43,3 +43,20 @@ export function Room() {
|
|||||||
</div>
|
</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 {
|
interface PeerRoomProps {
|
||||||
appId: string
|
appId: string
|
||||||
@ -8,17 +8,19 @@ interface PeerRoomProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function usePeerRoom({ appId, roomId }: PeerRoomProps) {
|
export function usePeerRoom({ appId, roomId }: PeerRoomProps) {
|
||||||
const peerRoom = useMemo(() => {
|
const [peerRoom, setPeerRoom] = useState<PeerRoom | null>(null)
|
||||||
const peerRoom = new PeerRoom({ appId }, roomId)
|
|
||||||
|
|
||||||
return peerRoom
|
useEffect(() => {
|
||||||
|
;(async () => {
|
||||||
|
setPeerRoom(await getPeerRoom({ appId }, roomId))
|
||||||
|
})()
|
||||||
}, [appId, roomId])
|
}, [appId, roomId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
peerRoom.leaveRoom()
|
peerRoom?.leaveRoom()
|
||||||
}
|
}
|
||||||
}, [appId, peerRoom, roomId])
|
}, [peerRoom])
|
||||||
|
|
||||||
return peerRoom
|
return peerRoom
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { joinRoom, Room, RoomConfig } from 'trystero'
|
import { joinRoom, Room, RoomConfig } from 'trystero'
|
||||||
|
|
||||||
|
import { sleep } from '../../utils'
|
||||||
|
|
||||||
export class PeerRoom {
|
export class PeerRoom {
|
||||||
private room: Room
|
private room: Room
|
||||||
|
|
||||||
@ -20,3 +22,25 @@ export class PeerRoom {
|
|||||||
return this.room.makeAction<T>(namespace)
|
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