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:
- Initiate Screen Sharing: A function within the hook should be exposed to trigger the screen sharing process. This will involve calling
navigator.mediaDevices.getDisplayMedia(). - Manage Stream: The hook should manage the
MediaStreamobject returned bygetDisplayMedia(). This stream should be accessible to the consuming component. - Track Sharing State: The hook should provide a boolean state variable to indicate whether screen sharing is currently active.
- Stop Screen Sharing: A function should be provided to gracefully stop the screen sharing session and release the resources.
- 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.
- 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
MediaStreamobject. - 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
MediaStreamobject should be properly handled to avoid memory leaks.
Edge Cases to Consider:
- Browser Support: The
getDisplayMediaAPI 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
startSharingis 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:
startSharingmight do nothing or throw an error.isSharingshould remainfalse.- 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), anderror(Error | null). - The hook must handle the cleanup of the
MediaStreamwhen the component unmounts or whenstopSharingis explicitly called. - Avoid using external libraries for the core screen sharing logic.
Notes
- The
navigator.mediaDevices.getDisplayMedia()API returns aPromise<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 theirstop()method. - You might want to listen for the
endedevent onMediaStreamTracks 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
MediaStreamto itssrcObjectproperty.