2015-09-01 10 views
5

Chociaż mój scenariusz jest dość szczegółowy, myślę, że przemawia on na większe pytanie w Flux. Składniki powinny być prostymi renderingami danych ze sklepu, ale co, jeśli komponent renderuje komponent innej firmy, który jest stanowy? W jaki sposób można wchodzić w interakcję z tym komponentem strony trzeciej, zachowując jednocześnie zgodność z regułami Flux?Jak przekazać akcję komponentowi React

Tak, mam aplikację React, która zawiera odtwarzacz wideo (przy użyciu clappr). Dobry przykład to szukanie. Po kliknięciu lokalizacji na pasku postępu chcę wyszukać odtwarzacz wideo. Oto, co mam teraz (przy użyciu RefluxJS). Próbowałem rozebrać mój kod na najbardziej odpowiednie części.

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

Błąd, który mam z tym kodem, polega na próbie dwukrotnego szukania w tym samym miejscu. Wyobraź sobie, że odtwarzacz wideo ciągle odtwarza. Kliknij pasek postępu, aby szukać. Nie poruszaj myszą i poczekaj kilka sekund. Kliknij pasek postępu ponownie w tym samym miejscu co poprzednio. Wartość data.seekTo nie uległa zmianie, więc window.player.seek nie jest wywoływana po raz drugi.

Rozważyłem kilka możliwości rozwiązania tego problemu, ale nie jestem pewien, co jest bardziej poprawne. o wejście ...


1: Reset seekTo po to służy

Wystarczy resetowania seekTo wydaje się najprostsze rozwiązanie, choć to na pewno nie bardziej elegancki. W ostatecznym rozrachunku jest to bardziej jak pomoc zespołowa.

To byłoby takie proste ...

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


2: Tworzenie osobnego magazynu, który działa bardziej jak pass-through

Zasadniczo będzie słuchać SeekStore, ale w rzeczywistości będzie działać jako przekaz, przez co będzie bardziej podobny do działania sklepu. To rozwiązanie wydaje się być hackem Flux, ale myślę, że zadziała.

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: Interakcja z window.player zasięgu moich działań

Kiedy o tym myślę, to czuje poprawne, ponieważ wywołanie window.player.seek jest w istocie działanie. Jedynym dziwnym aspektem jest to, że nie czuję się dobrze w interakcji z window wewnątrz akcji. Może to tylko irracjonalna myśl.

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, w pokoju jest inny słoń, którego nie dotknąłem. We wszystkich moich przykładach odtwarzacz jest przechowywany jako window.player.Clappr zrobił to automatycznie w starszych wersjach, ale chociaż od tego czasu został poprawiony do pracy z Browserify, nadal przechowujemy go na window (dług tech). Oczywiście moim trzecim rozwiązaniem jest wykorzystanie tego faktu, co technicznie jest złe. W każdym razie, zanim ktokolwiek to zauważy, zrozumie i zanotuje.


4: Szukajcie poprzez dispatchEvent

Rozumiem też, że wysyłki niestandardowe zdarzenie byłoby to zadanie, ale czuje się źle biorąc pod uwagę sposób mam Flux na miejscu. Wygląda na to, że wychodzę poza moją architekturę Flux, aby wykonać to zadanie. Powinienem móc to zrobić i pozostać na placu zabaw 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); 
    } 
}); 
+0

Chyba ostatnia pozycja skoczył powinny być częścią stanie się Twoja aplikacja, nie musi być w sklepie. Również gracz wygląda tak, jakby wstrzykiwał się w DOM bez React. To może być problem, ponieważ React chce kontrolować dom samodzielnie. – jnes

+0

@jnes, używamy również 'shouldComponentUpdate() {return false}', więc React nie dba o to, że gracz manipuluje DOMem w tym obszarze. Ponadto, dobry punkt o "seekTo" nie musi być w sklepie. Masz rację. Którą opcję wybrałbyś? –

+2

Jeff - jak zauważył Jnes, seekto naprawdę nie jest częścią stanu, ale CurrentPosition wydaje się być stanem. Próbujesz "wyrenderować" aktualną pozycję. Możesz wykonać akcję, która aktualizuje bieżącą pozycję podczas grania gracza, a gdy nazwiesz "szukanie", zresetowałbyś CurrentPosition do wybranej lokalizacji. Pozwala to na inne fajne rzeczy w przyszłości, takie jak przechowywanie ostatniej lokalizacji odtwarzanej przy niewielkiej zmianie interfejsu użytkownika. Szukanie akcji wymaga aktualizacji tylko wtedy, gdy seekTo! = CurrentPosition. Nie jestem zaznajomiony z ReFlux, więc nie jestem pewien, jak wdrożyć w ReFlux. \ –

Odpowiedz

1

5: utrzymać pozycję odtwarzania w stanie (jak zauważył Dan Kaufman)

można zrobić coś takiego:

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); 
    } 
} 
Powiązane problemy