虽然我的场景非常具体,但我认为它说明了 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
不调用第二次。
我已经考虑了一些解决这个问题的可能性,但我不确定哪个更正确。输入请求...
seekTo
1:使用后复位
简单地重置seekTo
似乎是最简单的解决方案,尽管它肯定不会更优雅。最终,这感觉更像是一种创可贴。
这就像...
window.player.on('player_seek', PlayerActions.resetSeek);
2:创建一个更像是传递的单独商店
基本上,我会听 a 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);
}
});
window.player
3:在我的行动中互动
当我想到它时,这感觉是正确的,因为调用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);
}
});