Last active
December 11, 2020 23:03
-
-
Save kn0ll/5f82bd04b995a2c5eb3d0c0ecb9da447 to your computer and use it in GitHub Desktop.
react port of MDN "A simple RTCDataChannel sample" https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Simple_RTCDataChannel_sample
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
// just a quick and dirty port, didn't really think it out much. just made this to help me conceptualize RTC | |
// (i was not happy with my findings) | |
import { useCallback, useMemo, useRef, useState } from "react"; | |
const WebRTCDemo: React.FC = () => { | |
const connection = useRef< | |
Partial<{ | |
localConnection: RTCPeerConnection; | |
sendChannel: RTCDataChannel; | |
remoteConnection: RTCPeerConnection; | |
receiveChannel: RTCDataChannel; | |
}> | |
>({}); | |
const [sendChannelReadyState, setSendChannelReadyState] = useState< | |
RTCDataChannelState | undefined | |
>(undefined); | |
const [messages, setMessages] = useState<string[]>([]); | |
const [inputMessage, setInputMessage] = useState(""); | |
const connectOnClick = useCallback(() => { | |
let receiveChannel: RTCDataChannel | undefined; | |
const localConnection = new RTCPeerConnection(); | |
const sendChannel = localConnection.createDataChannel("sendChannel"); | |
const handleSendChannelStatusChange = () => | |
setSendChannelReadyState(sendChannel.readyState); | |
const handleReceiveMessage = ({ data }: MessageEvent) => | |
setMessages((messages) => [...messages, data]); | |
const handleReceiveChannelStatusChange = () => { | |
if (receiveChannel) { | |
setSendChannelReadyState(receiveChannel.readyState); | |
console.log( | |
"Receive channel's status has changed to " + receiveChannel.readyState | |
); | |
} | |
// Here you would do stuff that needs to be done | |
// when the channel's status changes. | |
}; | |
const receiveChannelCallback = (event: RTCDataChannelEvent) => { | |
receiveChannel = event.channel; | |
receiveChannel.onmessage = handleReceiveMessage; | |
receiveChannel.onopen = handleReceiveChannelStatusChange; | |
receiveChannel.onclose = handleReceiveChannelStatusChange; | |
connection.current = { ...connection.current, receiveChannel }; | |
}; | |
sendChannel.onopen = handleSendChannelStatusChange; | |
sendChannel.onclose = handleSendChannelStatusChange; | |
const remoteConnection = new RTCPeerConnection(); | |
remoteConnection.ondatachannel = receiveChannelCallback; | |
const handleAddCandidateError = () => | |
console.log("Oh noes! addICECandidate failed!"); | |
const handleCreateDescriptionError = (error: Error) => | |
console.log("Unable to create an offer: " + error.toString()); | |
localConnection.onicecandidate = (e) => | |
!e.candidate || | |
remoteConnection | |
.addIceCandidate(e.candidate) | |
.catch(handleAddCandidateError); | |
remoteConnection.onicecandidate = (e) => | |
!e.candidate || | |
localConnection | |
.addIceCandidate(e.candidate) | |
.catch(handleAddCandidateError); | |
localConnection | |
.createOffer() | |
.then((offer) => localConnection.setLocalDescription(offer)) | |
.then(() => | |
remoteConnection.setRemoteDescription(localConnection.localDescription) | |
) | |
.then(() => remoteConnection.createAnswer()) | |
.then((answer) => remoteConnection.setLocalDescription(answer)) | |
.then(() => | |
localConnection.setRemoteDescription(remoteConnection.localDescription) | |
) | |
.catch(handleCreateDescriptionError); | |
connection.current = { | |
...connection.current, | |
localConnection, | |
sendChannel, | |
remoteConnection, | |
}; | |
}, []); | |
const disconnectOnClick = useCallback(() => { | |
const { | |
sendChannel, | |
receiveChannel, | |
localConnection, | |
remoteConnection, | |
} = connection.current; | |
sendChannel?.close(); | |
receiveChannel?.close(); | |
localConnection?.close(); | |
remoteConnection?.close(); | |
connection.current = {}; | |
setInputMessage(""); | |
setSendChannelReadyState(undefined); | |
}, []); | |
const sendOnClick = useCallback(() => { | |
setInputMessage(""); | |
connection.current.sendChannel?.send(inputMessage); | |
}, [inputMessage]); | |
const inputMessageOnChange = useCallback< | |
React.ChangeEventHandler<HTMLInputElement> | |
>((e) => { | |
setInputMessage(e.currentTarget.value); | |
}, []); | |
const ready = useMemo(() => sendChannelReadyState === "open", [ | |
sendChannelReadyState, | |
]); | |
return ( | |
<div> | |
<button onClick={connectOnClick} disabled={ready}> | |
Connect | |
</button> | |
<button onClick={disconnectOnClick} disabled={!ready}> | |
Disconnect | |
</button> | |
<button onClick={sendOnClick} disabled={!ready}> | |
Send | |
</button> | |
<input | |
type="text" | |
disabled={!ready} | |
value={inputMessage} | |
onChange={inputMessageOnChange} | |
/> | |
<ul> | |
{messages.map((message, idx) => ( | |
<li key={idx}>{message}</li> | |
))} | |
</ul> | |
</div> | |
); | |
}; | |
export default WebRTCDemo; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment