User Media Selection and Preview in React
Toby Allen
Solving your customer identity challenges with Auth0 - Senior Solutions Engineer | CISSP | CCSP
I've built a super simple Media Selection React Component. The component allows you to select which Media Input device you would use and then view or hear a preview of this input. This component could be adapted to use in any React project requiring media input selection.
The component leverages the browsers MediaDevice APIs, specifically enumerateDevices and getUserMedia. Additionally we watch for any changes, such as plugging a web cam in, with the ondevicechange event. I was inspired by Making a User Media Preview Component. I recommend that you head on over there for a short introduction to these apis.
I've put the code and the demo up on GitHub so can have a play around with it. Head to here to give it a go and check out media_selector.jsx for the code. The code itself is pretty straightforward but there are one or two things that can be a little tricky along the way. Firstly we set up the layout with a video element and a couple of drop down menus to allow selection of video or audio input. Initially, nothing is displayed and the lists are empty. Next, we get the list of devices by calling enumerateDevices in componentDidMount and we update the state. This in turn updates the lists thanks to the power of setState in React. We also add our event listener here to capture any device changes.
// Initialize after component creation
componentDidMount() {
//Get the devices from mediaDevices.
navigator.mediaDevices.enumerateDevices()
.then(devices => {
this.setState({devices: [devices]})
})
.catch(error => console.error(error))
//Add an event listener to monitor for changes to the device list.
navigator.mediaDevices.addEventListener('devicechange', navigator.mediaDevices.enumerateDevices()
.then(devices => (this.setState({devices: devices})))
.catch(error => console.error(error))
);
}
Most of the complexity is within the handleMediaChange function. This is called any time one of the options within the drop-downs is changed. Firstly we grab the existing audio or video devices and then update the appropriate one if there are any changes. Then we construct the appropriate constraints object. This is then fed into getUserMedia which returns a promise that resolves to a MediaStream object. The obvious thought with this is to update the state and leverage "srcObject" in the video element to display the video. This however doesn't work in React. Instead the returned MediaStream object needs to be converted to a URL using createObjectURL then you can simply update the src of the video element to point to the URL and the video displays this is shown below.
if (constraints.video || constraints.audio) {
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
this.setState({previewStream: window.URL.createObjectURL(stream)});
})
.catch(error => console.error(error))
}
As you can see from the code it actually seems to be cleaner and simpler in React than the original DoneJS/CanJS component that inspired me. ObviI hope you find this useful and good luck!