I a trying to convert the code in the ML5 image classification example(Link) to my React component, which is as follows:
class App extends Component {
video = document.getElementById('video');
state = {
result :null
}
loop = (classifier) => {
classifier.predict()
.then(results => {
this.setState({result: results[0].className});
this.loop(classifier) // Call again to create a loop
})
}
componentDidMount(){
ml5.imageClassifier('MobileNet', this.video)
.then(classifier => this.loop(classifier))
}
render() {
navigator.mediaDevices.getUserMedia({ video: true })
.then((stream) => {
this.video.srcObject = stream;
this.video.play();
})
return (
<div className="App">
<video id="video" width="640" height="480" autoplay></video>
</div>
);
}
}
export default App;
However this does not work. The error message says that Unhandled Rejection (TypeError): Cannot set property 'srcObject' of null
.
I can imagine video = document.getElementById('video');
is probably not able to grab the element by id. So I tried
class App extends Component {
video_element = <video id="video" width="640" height="480" autoplay></video>;
...
render() {
...
return (
<div className="App">
{video_element}
</div>
);
}
}
Which did not work either. I'm confused of what will be the correct method to implement this?
Any help appreciated, thanks!
I am answering again with an highlight on a slightly different problem rather then the ref
one.
There is a huge problem that is causing the awful blinking and constantly failing promise due to an exception... and it is the get of the user media in the render method!
Consider this, every time you set the state, the component is re-rendered. You have a loop that constantly update the state of the component, and that promise keeps failing.
You need to get the user media when the component gets mounted:
componentDidMount() {
navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
if (this.video.current) {
this.video.current.srcObject = stream;
this.video.current.play();
}
ml5.imageClassifier("MobileNet", this.video.current)
.then(classifier => this.loop(classifier));
});
}
Your render method is then a lot shorter:
render() {
return (
<div className="App">
<video ref={this.video} id="video" width="640" height="480" autoPlay />
</div>
)
}