【问题标题】:ES6 + React Component Instance MethodES6 + React 组件实例方法
【发布时间】:2016-04-12 10:23:11
【问题描述】:

我在 React 中制作了一个小的 Video 组件(你猜对了,播放视频),我想将该组件嵌入到父组件中,然后能够在视频组件上调用 play 方法.

我的视频组件看起来像:

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>
    );
  };
}

这真的很简单,这里没什么特别的。

现在在父组件中,我们称之为Page

export default class Page extends Component {
    video = (
        <Video source="some_url" type="video/mp4" />
    );

    render = () => {
        <div onClick={ this.video.play } />
    }
}

但是,如果我登录 .play,它是未定义的。

接下来我尝试将play 声明为Video 中的道具并放置一个默认道具,例如:

static defaultProps = {
    play: () => {
        const node = ReactDOM.findDOMNode(this);
    }
}

但在这种情况下,this 未定义。

在 React ES6 类上公开函数以便外部组件调用它的正确方法是什么?我应该在Video.prototype 上附加一些东西吗?

【问题讨论】:

    标签: javascript html reactjs ecmascript-6


    【解决方案1】:

    调用子组件的实例方法的正确方法是不做。 :-)

    这里有很多资源讨论了原因,但总而言之:它创建了一个不明确的数据流,它将组件耦合在一起,从而减少了关注点的分离,并且更难测试。

    做你想做的最好的方法是使用外部服务(例如事件发射器)来管理状态。在 Flux 中,这些将是“商店”。 Video 组件将根据其当前状态(例如 PLAYBACK_STARTED)触发操作,进而更新存储。 Page 组件可以触发 START_PLAYBACK 操作,这也会更新存储。两个组件都监听存储状态的变化,并做出相应的响应。例如:

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

    Flux 在这里不是必需的(例如,您可以使用 Redux 或根本不使用)。这里重要的是清晰的单向数据流。

    【讨论】:

    • 是的,我正在使用 Redux 和 React-Redux 来连接组件。因此,最好的方法是将保存视频的容器组件连接到 redux 存储,当我想播放视频时,只需发送一个动作。以及用于暂停等的单独操作。
    • 在我看来,这将是最好的方法。这似乎有点麻烦(对于一个小应用程序来说,可能是这样),但是当你的应用程序扩展一点时,它会更干净。例如,也许您稍后想要自动暂停视频以响应一些不相关的用户操作(例如打开菜单)。您可以通过参考传递该方法,但这很混乱且不清楚。最好调度一个动作 - 设置它并忘记它。
    • 嗯,很有趣,我确实更喜欢这种流程。然后,视频组件将只调用其父组件定义的道具上的函数,然后它们可以做任何他们想做的事情,例如执行调度。并且仅仅为了播放某个视频而发送一个动作并不难。理论上,您可以将视频源作为 currentVideosPlaying 存储在 redux 存储中,然后在连接的组件中匹配它们。
    • 我绝对认为这是最好的方法。我不介意使用另一个答案中建议的ref 版本,因为它确实可以正常工作,但对于大型应用程序来说这是一个混乱的架构。
    【解决方案2】:

    您可以使用 refs 将方法从子级传递给其父级。

    export default class Page extends Component {
        video = (
            <Video source="some_url" ref="video" type="video/mp4" />
        );
    
        render = () => {
            <div onClick={ this.refs.video.play } />
        }
    }
    

    来自Expose Component Functions

    【讨论】:

    • 嗯,这很有趣。所以我这样做了,当它是 onClick={ this.refs.video.play } 时它会因为一些奇怪的原因而崩溃,但是当我将 play 函数包装在另一个函数中时,一切都很好。
    • 它崩溃了,因为它将点击事件传递给了播放方法。相反,应该有一个调用播放方法的点击处理程序方法,这就是你所做的。
    • 最好更新答案以反映这些 cmets。即&lt;div onClick={()=&gt;this.refs.video.play} /&gt;
    猜你喜欢
    • 2016-10-15
    • 2019-08-20
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 2020-03-10
    相关资源
    最近更新 更多