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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user