如何将操作传递给React组件

时间:2015-09-01 20:33:06

标签: reactjs reactjs-flux flux refluxjs

虽然我的情景非常具体,但我认为它在Flux中提出了一个更大的问题。组件应该是来自商店的简单数据呈现,但是如果组件呈现有状态的第三方组件会怎样?如何在遵守Flux规则的同时与第三方组件进行交互?

所以,我有一个包含视频播放器的React应用程序(使用clappr)。一个很好的例子就是寻求。当我点击进度条上的某个位置时,我想要寻找视频播放器。这就是我现在所拥有的(使用RefluxJS)。我试图将我的代码删除到最相关的部分。

var PlayerActions = Reflux.createActions([
    'seek'
]);

var PlayerStore = Reflux.createStore({
    listenables: [
        PlayerActions
    ],

    onSeek(seekTo) {
        this.data.seekTo = seekTo;
        this.trigger(this.data);
    }
});

var Player = React.createClass({
    mixins: [Reflux.listenTo(PlayerStore, 'onStoreChange')],

    onStoreChange(data) {
        if (data.seekTo !== this.state.seekTo) {
            window.player.seek(data.seekTo);
        }

        // apply state
        this.setState(data);
    }

    componentDidMount() {
        // build a player
        window.player = new Clappr.Player({
            source: this.props.sourcePath,
            chromeless: true,
            useDvrControls: true,
            parentId: '#player',
            width: this.props.width
        });
    },

    componentWillUnmount() {
        window.player.destroy();
        window.player = null;
    },

    shouldComponentUpdate() {
        // if React realized we were manipulating DOM, it'd certainly freak out
        return false;
    },

    render() {
        return <div id='player'/>;
    }
});

我对此代码的错误是当您尝试两次寻找同一个地方时。想象一下视频播放器在不断播放。点击进度条进行搜索。不要移动鼠标,等待几秒钟。再次点击与之前相同位置的进度条。 data.seekTo的值没有变化,因此第二次不会调用window.player.seek

我已经考虑过一些解决这个问题的可能性,但我不确定哪个更正确。请求输入......


1:使用后重置seekTo

简单地重置seekTo似乎是最简单的解决方案,尽管它肯定不再优雅。最终,这感觉更像是一个创可贴。

这就像......一样简单。

window.player.on('player_seek', PlayerActions.resetSeek);


2:创建一个单独的商店,其行为更像是传递

基本上,我会倾听SeekStore,但实际上,这会作为传递,使其更像是商店的动作。这个解决方案感觉像是Flux的黑客,但我认为它会起作用。

var PlayerActions = Reflux.createActions([
    'seek'
]);

var SeekStore = Reflux.createStore({
    listenables: [
        PlayerActions
    ],

    onSeek(seekTo) {
        this.trigger(seekTo);
    }
});

var Player = React.createClass({
    mixins: [Reflux.listenTo(SeekStore, 'onStoreChange')],

    onStoreChange(seekTo) {
        window.player.seek(seekTo);
    }
});


3:在我的行动中与window.player互动

当我考虑它时,这个感觉正确,因为调用window.player.seek实际上是一个动作。唯一奇怪的是,我感觉不能在行动中与window进行交互。不过,也许这只是一种非理性的想法。

var PlayerActions = Reflux.createActions({
    seek: {asyncResult: true}
});
PlayerActions.seek.listen(seekTo => {
    if (window.player) {
        try {
            window.player.seek(seekTo);
            PlayerActions.seek.completed(err);
        } catch (err) {
            PlayerActions.seek.failed(err);
        }
    } else {
        PlayerActions.seek.failed(new Error('player not initialized'));
    }
});
顺便说一下,房间里还有另一头大象,我没有触及过。在我的所有示例中,播放器都存储为window.player。 Clappr在旧版本中自动执行此操作,但虽然已经修复了它与Browserify一起使用,但我们继续将其存储在window(技术债务)上。显然,我的第三个解决方案是利用这个事实,从技术上来说这是一件坏事。无论如何,在任何人指出这一点之前,理解并注意到它。


4:通过dispatchEvent寻找

我也明白,派遣一个自定义事件可以完成工作,但考虑到我有Flux,这感觉有点不对劲。这感觉就像我在我的Flux架构之外完成工作。我应该能够做到并留在Flux游乐场内。

var PlayerActions = Reflux.createActions({
    seek: {asyncResult: true}
});
PlayerActions.seek.listen(seekTo => {
    try {
        let event = new window.CustomEvent('seekEvent', {detail: seekTo});
        window.dispatchEvent(event);
        PlayerActions.seek.completed(err);
    } catch (err) {
        PlayerActions.seek.failed(err);
    }
});

var Player = React.createClass({
    componentDidMount() {
        window.addEventListener('seekEvent', this.onSeek);
    },
    componentWillUnmount() {
        window.removeEventListener('seekEvent', this.onSeek);
    },
    onSeek(e) {
        window.player.seek(e.detail);
    }
});

1 个答案:

答案 0 :(得分:1)

5:保持状态(如Dan Kaufman所示)

可以这样做:

handlePlay () {
  this._interval = setInterval(() => this.setState({curPos: this.state.curPos + 1}), 1000)
  this.setState({playing: true})  // might not be needed
}
handlePauserOrStop () {
  clearInterval(this._interval)
  this.setState({playing: false})
}
componentWillUnmount () {
  clearInteral(this._interval)
}
onStoreChange (data) {
  const diff = Math.abs(data.seekTo - this.state.curPos)
  if (diff > 2) {  // adjust 2 to your unit of time
    window.player.seek(data.seekTo);
  }
}