2014-12-02 6 views
7

Das Flux-Modell schreibt vor, dass Statusänderungen in den Ansichten ausgelöst werden, Aktionen ausgelöst werden, die über den Dispatcher ausgeführt werden, und in einem Geschäft zurück in Listening-Ansichten propagiert werden.In Flux/React.js, wo jQuery-Plugins initialisiert werden?

The Flux Model

Das ist alles schön und gut. Aber was, wenn ich eine bestimmte DOM-Operation nach einer bestimmten Änderung im Zustand durchführen muss? In der realen Welt Anwendungen, müssen wir manchmal verwenden, dass Sie wissen, Relikt genannt jQuery Plugins (nicht vergessen, die?)

Zum Beispiel muss ich ein jQuery-Video-Player initialisieren, à la $('#videoContainer').initPlayer(streamURL), nach eine Ajax-Anforderung zum AbrufenstreamURL. widmen Sie einen Moment Zeit, um herauszufinden, wie Sie tun würde, dass mit Flux/Reaktion, vor dem Lesen auf).

Auf der Ansicht Teil, könnte ich componentWillReceiveProps verwenden:

var jsVideoPlayer = React.createClass({ 
    componentWillReceiveProps: function(nextProps) { 
    $(this.refs.videoContainer.getDOMNode()).initPlayer(nextProps.streamURL); 
    }, 
    render: function() { 
    return (
     <div ref="videoContainer"> 
     </div> 
    ); 
    } 
}); 

Aber hier, würden die Spieler jedes Mal aktualisiert, um den Zustand initialisiert erhalten. Das würde unseren Videoplayer sicherlich kaputt machen. Lassen Sie uns überprüfen, ob sich die URL geändert hat, bevor wir etwas tun:

componentWillReceiveProps: function(nextProps) { 
    if (nextProps.streamURL && nextProps.streamURL !== this.streamURL) { 
     this.streamURL = nextProps.streamURL; 
     $(this.refs.videoContainer.getDOMNode()).initPlayer(nextProps.streamURL); 
    } 
    }, 

Einfach, oder? Aber was ist mit dem größeren Bild? Was ist, wenn wir das skalieren - und weiter -, wenn wir weitere Funktionen und UI-Elemente hinzufügen, die zu mehr benutzerdefinierten Validierungscode in den Ansichten führen könnten, was aus dem Geschäft kommt. Selbst in einer perfekten App, in der jeder Laden seine eigene Domain hat und alles blitzblank in Designmustern auf einer Ebene liegt, die einer engagierten religiösen Anhängerschaft würdig ist, müssten wir immer mehr Logik in den Ansichten verwenden, wie die App skalieren würde .

In unserem Team habe ich argumentiert, diese Logik wirklich in eine ControllerView gehen sollte, die den Flux-Modell passt - aber jeder argumentiert sonst etwas, ist der Rahmen, der dafür sorgen sollte. Wir sind neu in Flux/React und konnten uns keine bessere Idee ausdenken, wie wir das Framework für uns machen können.

+0

warum nicht nur in die Komponente componentDidMount oder componentDidUpdate? – zackify

+0

@zackify, weil es von einer Ajax-Anfrage abhängig ist, die nur auftritt, wenn ein Benutzer auf eine Schaltfläche klickt – pilau

+0

Warum nicht einfach eine Aktion wie VIDEO_URL_RETRIEVED verwenden, um einen Status in Ihrem Geschäft zu aktualisieren? Ihre controller-view-Komponente erhält den aktualisierten Status und in Ihrem jsVideoPlayer, wenn die Props streamUrl nicht null ist, können Sie Ihr jQuery-Plugin initialisieren. Wenn Ihre Komponente eine reine Rendermethode hat, wird sie nur einmal aktualisiert: wenn die Aktion VIDEO_URL_RETRIEVED ausgelöst wird. – Pcriulan

Antwort

5

Wenn Sie identifizieren Muster, die Sie Abstraktionen erstellen können. Hier ist das Muster die Notwendigkeit, wechselnde Requisiten auf einfache Weise zu handhaben. Ich habe eine Weile über dieses Problem nachgedacht ... das ist es, was ich bisher herausgefunden habe.

Es kann immer noch ausführlich sein, aber es ist deklarativer und erfordert nicht viele if-Anweisungen und Vergleiche. jsbin

var Test = React.createClass({ 
    mixins: [propUpdatesMixin], 
    propUpdates: { 
    text: { 
     onChange: function(from, to, el){ 
     el.textContent = to; 
     }, 
     idempotent: true 
    } 
    }, 
    render: function(){ 
    return <div />; 
    } 
}); 

onChange wird nur aufgerufen, wenn die Eigenschaft tatsächlich ändert. Standardmäßig wird dies mit === verglichen. Sie können jedoch eine compare von "shallow", "deep" oder eine Funktion angeben, die die alten und neuen Prop-Werte übernimmt und true zurückgibt, wenn sie gleich sind.

Wenn idempotent wahr ist, wird onChange anstelle von onMount mit dem ersten Argument undefined aufgerufen. Inside onChange und onMount this ist die Komponenteninstanz. Verwenden Sie das letzte Argument (config), um das Objekt mit den onChange/onMount/etc-Eigenschaften abzurufen.


Manchmal braucht man mehr Kontrolle, wie wenn Sie brauchen Wechsel auf mehrere Requisiten zu reagieren.

componentDidUpdate: function(prevProps){ 
    var changes = this.getPropsChanges(prevProps, this.props); 
    if (changes.text.changed && changes.color.changed) { 
    // ... 
    } 
} 

Die Rückkehr von getPropsChanges ist Abbildung von prop Namen Objekte zu ändern. Ändern Objekte haben diese Struktur:

{ 
    changed: Boolean, 
    from: Any, 
    to: Any, 
    name: String (prop name) 
    config: {onMount,onChange,idempotent,compare} 
} 

Die mixin wurde entwickelt, um nicht in die Quere zu kommen. Sie können Strategien kombinieren und anpassen. Es erfordert nur ein propUpdates, aber es kann {} sein. Sie können immer noch Ihr eigenes componentDidMount schreiben und propUpdates nur für onChange verwenden, oder nur propUpdates für onMount verwenden und Ihre eigene KomponenteWillUpdate schreiben, oder onChange für eine Requisite und onMount für eine andere und ein idempotentes onChange für eine dritte haben und Dinge tun in componentDidMount und componentDidUpdate.

Spielen Sie mit der jsbin herum, um zu sehen, ob es Ihren Bedürfnissen entspricht. Der Vollständigkeit halber ist hier der vollständige Code für das Mixin.

var comparisons={"shallow":function(a,b){return Object.keys(a).every(function(key){return a[key]!==b[key]})},"deep":function(a,b){return!_.isEqual(a,b)},"default":function(a,b){return a!==b}};var EACH_PROP="__propUpdatesMixin_eachProp__";var PROPS="propUpdates";var propUpdatesMixin={}; 
propUpdatesMixin.componentDidMount=function(){var el=this.getDOMNode();this[EACH_PROP](function(propName,config,propValue){if(config.onMount)config.onMount.call(this,propValue,el,config);else if(config.onChange&&config.idempotent)config.onChange.call(this,undefined,propValue,el,config)},this.props)}; 
propUpdatesMixin.componentDidUpdate=function(prevProps){var el=this.getDOMNode();var changes=this.getPropsChanges(prevProps,this.props);Object.keys(changes).forEach(function(propName){var change=changes[propName];if(change.changed&&change.config.onChange)change.config.onChange.call(this,change.from,change.to,el,change.config)},this)}; 
propUpdatesMixin.getPropsChanges=function(propsA,propsB){var updates={};this[EACH_PROP](function(propName,config,a,b){var compare=typeof config.compare==="function"?config.compare:comparisons[config.compare]?comparisons[config.compare]:comparisons["default"];var changed=compare(a,b);updates[propName]={changed:changed,from:a,to:b,name:propName,config:config}},propsA,propsB);return updates}; 
propUpdatesMixin[EACH_PROP]=function(fn,propsA,propsB){propsA=propsA||{};propsB=propsB||{};Object.keys(this[PROPS]).map(function(propName){return fn.call(this,propName,this[PROPS][propName],propsA[propName],propsB[propName])},this)}; 
+1

Das ist eigentlich ziemlich gut und sehr beeindruckend. Es zeigt, was ich glaube, wie das React-Universum Entwickler dazu bringt, mehr über ihre Architektur nachzudenken. Vielen Dank für die JSBIN - was für eine ausgezeichnete Antwort. Ich bin versucht, es als korrekt zu markieren, aber ich möchte mehr Meinungen bekommen :) – pilau

Verwandte Themen