Hone logo
Hone
Problems

Implement a useScreenShare Hook for React

In modern web applications, particularly those involving video conferencing, collaboration, or presentations, the ability to share one's screen is a crucial feature. This challenge requires you to build a custom React hook, useScreenShare, that encapsulates the logic for initiating, managing, and stopping screen sharing using the browser's MediaDevices API.

Problem Description

Your task is to create a reusable React hook named useScreenShare that provides a clean interface for integrating screen sharing functionality into any React component. This hook should handle the complexities of interacting with the navigator.mediaDevices.getDisplayMedia API, including requesting user permission, receiving the screen share stream, and releasing the stream when it's no longer needed.

Key Requirements:

  1. Initiate Screen Sharing: A function within the hook should be exposed to trigger the screen sharing process. This will involve calling navigator.mediaDevices.getDisplayMedia().
  2. Manage Stream: The hook should manage the MediaStream object returned by getDisplayMedia(). This stream should be accessible to the consuming component.
  3. Track Sharing State: The hook should provide a boolean state variable to indicate whether screen sharing is currently active.
  4. Stop Screen Sharing: A function should be provided to gracefully stop the screen sharing session and release the resources.
  5. Handle Permissions and Errors: The hook should gracefully handle potential errors during the process, such as the user denying permission or the browser not supporting the API.
  6. Cleanup: Ensure that the screen share stream is stopped and any associated resources are released when the component using the hook unmounts.

Expected Behavior:

  • When the "start sharing" function is called, the browser should prompt the user to select a screen or application to share.
  • If the user grants permission, the hook should update its internal state to reflect that sharing is active and provide the MediaStream object.
  • If the user denies permission or an error occurs, the hook should reflect an inactive state and potentially expose an error message.
  • When the "stop sharing" function is called, the screen sharing should cease, and the hook's state should be updated accordingly.
  • The MediaStream object should be properly handled to avoid memory leaks.

Edge Cases to Consider:

  • Browser Support: The getDisplayMedia API might not be available in all browsers or older versions.
  • User Denial: The user might explicitly deny permission to share their screen.
  • Interrupted Sharing: The sharing might be stopped by the user outside of the hook's control (e.g., by clicking a "Stop Sharing" button in the browser's UI). The hook should ideally detect this.
  • Multiple Calls: What happens if startSharing is called multiple times in quick succession?

Examples

Example 1: Basic Usage

// Assuming a component where this hook is used:
function MyComponent() {
  const {
    isSharing,
    startSharing,
    stopSharing,
    stream,
    error,
  } = useScreenShare();

  return (
    <div>
      {error && <p>Error: {error.message}</p>}
      <button onClick={startSharing} disabled={isSharing}>
        Start Screen Share
      </button>
      <button onClick={stopSharing} disabled={!isSharing}>
        Stop Screen Share
      </button>

      {isSharing && stream && (
        <video autoPlay ref={(videoRef) => {
          if (videoRef && stream) {
            videoRef.srcObject = stream;
          }
        }} />
      )}
    </div>
  );
}

Example 2: Handling No Support

If navigator.mediaDevices.getDisplayMedia is not available:

  • startSharing might do nothing or throw an error.
  • isSharing should remain false.
  • An appropriate error message might be returned or logged.

Example 3: Stream Termination Detection

If the user stops sharing via the browser's native UI, the isSharing state should eventually become false, and the stream should be nullified. This often requires listening for the ended event on the MediaStreamTracks.

Constraints

  • The solution must be implemented in TypeScript.
  • The hook should leverage the navigator.mediaDevices.getDisplayMedia() API.
  • The hook should return an object with at least isSharing (boolean), startSharing (function), stopSharing (function), stream (MediaStream | null), and error (Error | null).
  • The hook must handle the cleanup of the MediaStream when the component unmounts or when stopSharing is explicitly called.
  • Avoid using external libraries for the core screen sharing logic.

Notes

  • The navigator.mediaDevices.getDisplayMedia() API returns a Promise<MediaStream>. You'll need to handle this asynchronous operation.
  • Remember to consider the different types of tracks that might be present in the MediaStream (e.g., video tracks from the screen).
  • When stopping the stream, iterate through the MediaStreamTracks and call their stop() method.
  • You might want to listen for the ended event on MediaStreamTracks to detect when sharing is stopped by the user outside of your hook's direct control. This can help keep the hook's state synchronized.
  • For a video element to display the screen share, you'll need to assign the MediaStream to its srcObject property.
Loading editor...
typescript