Hone logo
Hone
Problems

Implementing a useWebRTC Hook for Simplified Peer-to-Peer Communication in React

WebRTC (Web Real-Time Communication) enables direct audio/video communication between browsers without requiring an intermediary server. This challenge asks you to create a reusable useWebRTC hook in React that simplifies the complexities of WebRTC setup, including obtaining media streams, establishing peer connections, and handling signaling. This hook will abstract away much of the boilerplate code, allowing developers to focus on the application logic of their WebRTC-powered features.

Problem Description

You are tasked with creating a useWebRTC hook in React that manages the lifecycle of a WebRTC peer connection. The hook should handle:

  1. Media Stream Acquisition: Requesting and managing the user's audio and/or video stream. The user should be able to specify whether to request audio, video, or both.
  2. Peer Connection Management: Creating and managing a RTCPeerConnection object.
  3. Signaling Channel: Providing functions for sending and receiving SDP (Session Description Protocol) offers and answers, as well as ICE (Interactive Connectivity Establishment) candidates. These functions will be used to communicate with a remote peer.
  4. Connection State Tracking: Tracking the state of the peer connection (e.g., connecting, connected, disconnected) and providing this information to the component using the hook.
  5. Cleanup: Closing the peer connection and releasing the media stream when the component unmounts.

Key Requirements:

  • The hook must be written in TypeScript.
  • The hook should accept an optional signalHandler function as an argument. This function will be called with the sendSignal function, allowing the component to integrate with a signaling server.
  • The hook should return an object containing:
    • stream: The local media stream (or null if not available).
    • peerConnection: The RTCPeerConnection object (or null if not initialized).
    • sendSignal: A function to send signaling messages (SDP offers/answers, ICE candidates) to the remote peer.
    • connectionState: A string representing the current state of the peer connection (e.g., "connecting", "connected", "disconnected").
    • error: An error object if an error occurred during initialization or connection.
    • createOffer: A function to create an SDP offer.
    • createAnswer: A function to create an SDP answer.
    • addIceCandidate: A function to add an ICE candidate to the peer connection.
    • closeConnection: A function to close the peer connection and release the stream.

Expected Behavior:

  • When the hook is first mounted, it should request the user's media stream.
  • If the media stream is successfully obtained, the stream property should be updated.
  • The connectionState property should reflect the current state of the peer connection.
  • The sendSignal function should be used to send signaling messages to the remote peer.
  • When the component is unmounted, the hook should close the peer connection and release the media stream.
  • Error handling should be robust, and any errors should be reported through the error property.

Edge Cases to Consider:

  • User denies media access.
  • Browser does not support WebRTC.
  • Network connectivity issues.
  • Signaling server errors.
  • Peer connection errors.

Examples

Example 1:

Input:  `useWebRTC({ audio: true, video: false, signalHandler: (sendSignal) => { /* Implement signaling logic here */ } })`
Output: `{ stream: MediaStream, peerConnection: RTCPeerConnection, sendSignal: (sdp: RTCSessionDescription | RTCIceCandidate) => void, connectionState: 'connecting' | 'connected' | 'disconnected', error: null, createOffer: () => Promise<void>, createAnswer: () => Promise<void>, addIceCandidate: (candidate: RTCIceCandidate) => void, closeConnection: () => void }`
Explanation: The hook requests only audio, initializes a peer connection, and provides functions for signaling and connection management. The initial connection state is 'connecting'.

Example 2:

Input: `useWebRTC({ audio: true, video: true, signalHandler: null })`
Output: `{ stream: MediaStream, peerConnection: RTCPeerConnection, sendSignal: undefined, connectionState: 'connecting' | 'connected' | 'disconnected', error: null, createOffer: () => Promise<void>, createAnswer: () => Promise<void>, addIceCandidate: (candidate: RTCIceCandidate) => void, closeConnection: () => void }`
Explanation: The hook requests both audio and video, initializes a peer connection, and provides functions for signaling and connection management.  The `signalHandler` is null, so `sendSignal` is undefined.

Example 3: (Edge Case)

Input: `useWebRTC({ audio: true, video: true, signalHandler: (sendSignal) => { /* Implement signaling logic here */ } })` (User denies media access)
Output: `{ stream: null, peerConnection: null, sendSignal: (sdp: RTCSessionDescription | RTCIceCandidate) => void, connectionState: 'disconnected', error: Error('User denied media access'), createOffer: () => Promise<void>, createAnswer: () => Promise<void>, addIceCandidate: (candidate: RTCIceCandidate) => void, closeConnection: () => void }`
Explanation: The user denies media access. The hook initializes but fails to obtain a stream, resulting in `stream: null`, `peerConnection: null`, and an error indicating the user denied media access.

Constraints

  • The hook must be compatible with modern browsers that support WebRTC.
  • The hook should handle errors gracefully and provide informative error messages.
  • The hook should be reasonably performant and avoid unnecessary computations.
  • The sendSignal function should accept either an RTCSessionDescription (for SDP offers/answers) or an RTCIceCandidate.
  • The createOffer, createAnswer, and addIceCandidate functions should be asynchronous and return Promises.

Notes

  • Consider using async/await for asynchronous operations.
  • Pay close attention to error handling and ensure that errors are properly reported.
  • The signaling logic is not part of this challenge; the hook only provides the functions for sending and receiving signals. The component using the hook is responsible for implementing the signaling logic.
  • You can use a library like react-webcam to simplify media stream acquisition, but it's not required. The core requirement is to demonstrate understanding of WebRTC concepts and how to integrate them into a React hook.
  • Focus on creating a clean, reusable, and well-documented hook.
Loading editor...
typescript