简体   繁体   中英

ES6 + React Component Instance Method

I'm making a small Video component in React (for you guessed it, playing videos) and I want to embed that component inside a parent component and then be able to call a play method on the video component.

My video component 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 component, 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 components? Should I attach something to Video.prototype ?

The correct way to call an instance method of a child component 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 components 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 (eg event emitter) to manage the state. In Flux, these would be "stores". The Video component would trigger actions based on its current state (eg PLAYBACK_STARTED ), which would in turn update the store. The Page component can fire a START_PLAYBACK action, which would also update the store. Both components listen for changes in the store's state, and respond accordingly. Eg:

Page -> START_PLAYBACK -> Video (play) -> PLAYBACK_STARTED -> Page (update ui)

Flux is not a requirement here (eg 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM