2015-09-01 10 views
5

Während mein Szenario ziemlich spezifisch ist, denke ich, dass es zu einer größeren Frage in Flux spricht. Komponenten sollten einfache Renderings von Daten aus einem Geschäft sein, aber was ist, wenn Ihre Komponente eine Drittanbieterkomponente darstellt, die statusbehaftet ist? Wie interagiert man mit dieser Komponente von Drittanbietern, während die Regeln von Flux eingehalten werden?Wie eine Aktion zu einer React-Komponente zu kommunizieren

Also, ich habe eine React App, die einen Videoplayer enthält (mit clappr). Ein gutes Beispiel ist das Suchen. Wenn ich auf einen Standort in der Fortschrittsleiste klicke, suche ich den Video-Player. Hier ist, was ich gerade habe (mit RefluxJS). Ich habe versucht, meinen Code auf die relevantesten Teile zu strippen.

Der Fehler, den ich mit diesem Code habe, ist, wenn Sie versuchen, den gleichen Ort zweimal zu suchen. Stellen Sie sich vor, der Videoplayer spielt ständig. Klicken Sie auf den Fortschrittsbalken, um zu suchen. Bewegen Sie die Maus nicht und warten Sie ein paar Sekunden. Klicken Sie erneut auf den Fortschrittsbalken an der gleichen Stelle wie zuvor. Der Wert von data.seekTo hat sich nicht geändert, daher wird window.player.seek nicht zum zweiten Mal aufgerufen.

Ich habe ein paar Möglichkeiten in Betracht gezogen, um dies zu lösen, aber ich bin mir nicht sicher, was ist richtiger. Eingabe angefordert ...


1: seekTo zurücksetzen, nachdem es

einfach verwendet wird Zurücksetzen seekTo wie die einfachste Lösung scheint, obwohl es sicherlich nicht mehr elegant ist. Letztendlich fühlt sich das eher wie ein Pflaster an.

Dies würde als so einfach sein ...

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


2: Erstellen Sie ein separates Geschäft, das mehr wie ein Pass-Through-I Grundsätzlich

wirkt, würde auf eine SeekStore hören, aber in Wirklichkeit würde dies als eine Durchleitung, so dass es eher wie eine Aktion, die ein Geschäft. Diese Lösung fühlt sich an wie ein Hack von Flux, aber ich denke, es würde funktionieren.

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: Interagieren mit window.player in meinen Handlungen

Wenn ich darüber nachdenke, diese fühlt richtig, da window.player.seek Aufruf ist eine Aktion in der Tat. Das einzige komische Etwas ist, dass ich mich nicht richtig mit window in den Aktionen fühle. Vielleicht ist das aber nur ein irrationaler Gedanke.

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

BTW, gibt es einen ganzen anderen Elefanten im Raum, dass ich nicht auf berührt habe. In allen meinen Beispielen wird der Player als window.player gespeichert.Clappr hat dies in älteren Versionen automatisch gemacht, aber obwohl es inzwischen für die Arbeit mit Browserify repariert wurde, speichern wir es weiterhin auf der window (Tech Debt). Offensichtlich nutzt meine dritte Lösung diese Tatsache, die technisch gesehen eine schlechte Sache ist. Wie auch immer, bevor jemand darauf hinweist, verstanden und zur Kenntnis genommen wird.


4: Suche über dispatchEvent

Ich verstehe auch, dass ein benutzerdefiniertes Ereignis Dispatching würde den Job zu erledigen, aber das fühlt sich an Art und Weise falsch Ich habe Flux an Ort und Stelle unter Berücksichtigung. Es fühlt sich an, als würde ich meine Flux-Architektur verlassen, um den Job zu erledigen. Ich sollte es schaffen und im Flux-Spielplatz bleiben.

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

Es sei denn, die letzte Position sollte Teil gesprungen sein Ihr App-Status muss nicht in einem Geschäft sein. Auch sieht der Player so aus, als ob er sich ohne React in das DOM injiziert. Dies könnte ein Problem sein, da React das dom selbst kontrollieren möchte. – jnes

+0

@jnes verwenden wir auch 'shouldComponentUpdate() {return false}', so dass es React egal ist, dass der Player das DOM in diesem Bereich manipuliert. Auch ein guter Punkt über 'seekTo' muss nicht im Laden sein. Du hast Recht. Welche Option würden Sie wählen? –

+2

Jeff - wie jnes darauf hingewiesen hat, ist seektro wirklich kein Teil des Staates, aber CurrentPosition scheint Staat zu sein. Sie versuchen, die aktuelle Position zu "rendern". Sie können eine Aktion ausführen, die die aktuelle Position aktualisiert, während der Player spielt, und wenn Sie "seek" aufrufen, wird die CurrentPosition auf die gewünschte Position zurückgesetzt. Dies ermöglicht in Zukunft weitere nette Dinge wie das Speichern des zuletzt gespielten Ortes mit wenig Veränderung der Benutzeroberfläche. Suchaktion müsste nur aktualisiert werden, wenn seekTo! = CurrentPosition. Ich bin nicht vertraut mit ReFlux, also bin ich mir nicht sicher, wie in ReFlux zu implementieren. \ –

Antwort

1

5: Halten Sie die Abspielposition im Zustand (wie von Dan Kaufman angegeben)

Könnte so etwas getan werden:

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