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);
}
});
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
@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ś? –
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. \ –