I'm making a small Video
ponent in React (for you guessed it, playing videos) and I want to embed that ponent inside a parent ponent and then be able to call a play
method on the video ponent.
My video ponent looks like:
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
const { string, func } = PropTypes;
export default class Video extends Component {
static propTypes = {
source: string.isRequired,
type: string.isRequired,
className: string
};
play = () => {
};
render = () => {
const { className } = this.props;
return (
<video className={ className } width="0" height="0" preload="metadata">
<source src={ this.props.source } type={ this.type } />
Your browser does not support the video tag.
</video>
);
};
}
It's really simple, nothing fancy going on here.
Now in the parent ponent, lets call it Page
:
export default class Page extends Component {
video = (
<Video source="some_url" type="video/mp4" />
);
render = () => {
<div onClick={ this.video.play } />
}
}
However if I log .play
it's undefined.
Next I tried declaring play
as a prop in Video
and putting a default prop like:
static defaultProps = {
play: () => {
const node = ReactDOM.findDOMNode(this);
}
}
But in this context, this
in undefined.
What is the proper way to expose a function on a React ES6 class so that it can be called by external ponents? Should I attach something to Video.prototype
?
I'm making a small Video
ponent in React (for you guessed it, playing videos) and I want to embed that ponent inside a parent ponent and then be able to call a play
method on the video ponent.
My video ponent looks like:
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
const { string, func } = PropTypes;
export default class Video extends Component {
static propTypes = {
source: string.isRequired,
type: string.isRequired,
className: string
};
play = () => {
};
render = () => {
const { className } = this.props;
return (
<video className={ className } width="0" height="0" preload="metadata">
<source src={ this.props.source } type={ this.type } />
Your browser does not support the video tag.
</video>
);
};
}
It's really simple, nothing fancy going on here.
Now in the parent ponent, lets call it Page
:
export default class Page extends Component {
video = (
<Video source="some_url" type="video/mp4" />
);
render = () => {
<div onClick={ this.video.play } />
}
}
However if I log .play
it's undefined.
Next I tried declaring play
as a prop in Video
and putting a default prop like:
static defaultProps = {
play: () => {
const node = ReactDOM.findDOMNode(this);
}
}
But in this context, this
in undefined.
What is the proper way to expose a function on a React ES6 class so that it can be called by external ponents? Should I attach something to Video.prototype
?
2 Answers
Reset to default 6The correct way to call an instance method of a child ponent is to not do it. :-)
There are many resources here that talk about why, but to summarize: it creates an unclear data flow, it couples ponents together which decreases separation of concerns, and it is harder to test.
The best way to do what you want is to use an external service (e.g. event emitter) to manage the state. In Flux, these would be "stores". The Video
ponent would trigger actions based on its current state (e.g. PLAYBACK_STARTED
), which would in turn update the store. The Page
ponent can fire a START_PLAYBACK
action, which would also update the store. Both ponents listen for changes in the store's state, and respond accordingly. E.g.:
Page -> START_PLAYBACK -> Video (play) -> PLAYBACK_STARTED -> Page (update ui)
Flux is not a requirement here (e.g. you could use Redux or nothing at all). What's important here is a clear, unidirectional data flow.
You can use refs for passing a method from a child to its parent.
export default class Page extends Component {
video = (
<Video source="some_url" ref="video" type="video/mp4" />
);
render = () => {
<div onClick={() => this.refs.video.play()} />
}
}
From Expose Component Functions