React: Wie implementiere ich shouldComponentUpdate mit this.context?

Erstellt am 13. Nov. 2014  ·  126Kommentare  ·  Quelle: facebook/react

Ich weiß, dass this.context nicht offiziell da ist, aber einige Bibliotheken verlassen sich darauf, und es scheint, als würde es mit # 2509 in Form kommen.

Ich versuche zu verstehen, wie genau shouldComponentUpdate Berücksichtigung von context implementiert werden soll. Ich habe festgestellt, dass es ein drittes Argument akzeptiert ( nextContext ) und ich kann PureRenderMixin , um es auch zu überprüfen:

  shouldComponentUpdate: function(nextProps, nextState, nextContext) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState) ||
           !shallowEqual(this.context, nextContext); // this will throw without context, read on
  }

Komponenten, die sich nicht für this.context indem sie contextTypes nicht weglassen, erhalten dieses dritte Argument nicht, was verständlich ist.

Dies stellt jedoch ein Problem dar, wenn wir eine <Middle /> -Komponente zwischen <Top /> Kontextbesitzer und <Bottom /> Kontextkonsument haben. Wenn <Middle /> ein restriktives shouldComponentUpdate <Middle /> implementiert, kann shouldComponentUpdate <Bottom /> auf die Kontextaktualisierungen von <Top /> reagieren:

( Geige )

var Bottom = React.createClass({
  contextTypes: {
    number: React.PropTypes.number.isRequired
  },

  render: function () {
    return <h1>{this.context.number}</h1>
  }
});

var Middle = React.createClass({
  shouldComponentUpdate: function (nextProps, nextState, nextContext) {
    return false;
  },

  render: function () {
    return <Bottom />;
  }
});

var Top = React.createClass({
  childContextTypes: {
    number: React.PropTypes.number.isRequired
  },

  getInitialState: function () {
    return { number: 0 };
  },

  getChildContext: function () {
    return { number: this.state.number };
  },

  componentDidMount: function () {
    setInterval(function () {
      this.setState({
        number: this.state.number + 1
      });
    }.bind(this), 1000);
  },

  render: function() {
    return <Middle />;    
  }
});

React.render(<Top />, document.body);

Das gleiche Problem würde auftreten, wenn ich versuchen würde, Middle ein generisches kontextsensitives shouldComponentUpdate wie ich oben geschrieben habe, weil Middle kein this.context sei denn, es entscheidet sich dafür im.

Dies kann umgangen werden, indem contextTypes zu Middle hinzugefügt wird, aber es sieht nicht nach einer guten Lösung aus. Sie müssten auf jeder Ebene mit smart shouldComponentUpdate explizit die erforderlichen contextTypes hinzufügen, damit es zu einfach ist, auszufallen.

Wird dies durch # 2112 gelöst? Gibt es in der Zwischenzeit eine andere Lösung? Was ist der empfohlene Weg?

Component API Bug

Hilfreichster Kommentar

Ich frage mich, ob die Kontextausbreitung in einer separaten Baumdurchquerung stattfinden könnte, ohne von der falschen shouldComponentUpdate in der Mitte blockiert zu werden.

Wenn sich der Kontext eines Elternteils ändert, sollten grundsätzlich alle Nachkommen, die diesen Kontext erhalten, als schmutzig markiert werden, egal wie weit entfernt. Die Kontextänderung des Vorfahren sollte den gleichen Effekt haben wie eine Statusänderung für Nachkommen, die sich für diesen Kontext entscheiden - sie sollten unabhängig von den Aussagen ihrer Eltern neue context .

Alle 126 Kommentare

Dies ist eine sehr gute Frage. Danke, dass du es angesprochen hast!

Dies ist ein Fall, in dem Sie die Baumneuberechnung manuell beschneiden / optimieren. Mir scheint, Sie müssten den Zugriff auf alle Kontextvariablen anfordern, die für Ihre Berechnung relevant sind. Wir könnten auch in Betracht ziehen, andere ausgefallene Dinge zu tun, wie bei Bedarf einen Hash des vollständigen Kontexts (oder sogar des vollständigen Kontexts) zu übergeben.

@sebmarkbage - Gedanken?

Ja, das Problem ist, dass die mittleren Komponenten nicht wissen, welchen Kontext ein entferntes Kind benötigen könnte. Sie können auf keinen Fall wissen, welche contextTypes zu deklarieren sind, um shouldComponentUpdate korrekt implementieren zu können.

Ich frage mich, ob die Kontextausbreitung in einer separaten Baumdurchquerung stattfinden könnte, ohne von der falschen shouldComponentUpdate in der Mitte blockiert zu werden.

Wenn sich der Kontext eines Elternteils ändert, sollten grundsätzlich alle Nachkommen, die diesen Kontext erhalten, als schmutzig markiert werden, egal wie weit entfernt. Die Kontextänderung des Vorfahren sollte den gleichen Effekt haben wie eine Statusänderung für Nachkommen, die sich für diesen Kontext entscheiden - sie sollten unabhängig von den Aussagen ihrer Eltern neue context .

@gaearon Ich denke, in diesem Fall müssen Sie alles neu rendern, sodass shouldComponentUpdate keine Auswirkungen auf das Beschneiden von Teilbäumen hat. Andernfalls befindet sich der Kontext in einem inkonsistenten Zustand mit dem Elementbaum.

@andreypopp

Ich denke, dass shouldComponentUpdate für mittlere Komponenten keinen Einfluss darauf haben sollte, ob ihre Nachkommen den neuen Kontext erhalten, genauso wie es keinen Einfluss darauf hat, ob sie einen neuen Status erhalten:

http://jsbin.com/geseduneso/2/edit?js , Ausgabe

(Wenn dies der Fall wäre, würden beide Zahlen inkrementieren)

Wo ist die Inkonsistenz?

Um es anders auszudrücken, ich denke, context sollte genauso funktionieren, als ob der Kontexteigentümer so etwas wie einen "Laden" hätte, in den das Ergebnis seiner getChildContext() in componentWillUpdate wird. und alle Konsumenten von Nachkommenkontexten, die Schlüssel aus diesem Kontext in contextTypes deklariert haben, sollten diesen Kontext so erhalten, als ob sie diesen „Speicher“ abonniert und ihren eigenen Status aktualisiert hätten.

Ich meine nicht die eigentliche Implementierung - aber ich möchte zeigen, dass dieses Modell nicht inkonsistenter ist als jede Flux-App mit shouldComponentUpdate in der Mitte. Der Kontext sollte sich wie ein „Seitwärtsspeicher“ verhalten, sich jedoch auf einen Teilbaum beschränken.

Bearbeiten: Dies funktioniert nicht, da sich die übergeordneten Elemente möglicherweise ändern

Ich habe mit Glenjamin im IRC gesprochen und er hat mich überzeugt, dass es eine schlechte Idee sein könnte, den Kontext zu ändern. Sie verlieren die Möglichkeit, darüber nachzudenken, warum etwas aktualisiert wurde, wenn ein Root-Update implizit unterschiedliche untergeordnete Updates verursacht.

Aber die einzig vernünftige Lösung, die ich sehe, besteht darin, Kontextänderungen vollständig zu verbieten. Das heißt, machen Sie getChildContext() ähnlich wie getInitialState() , das nur einmal aufgerufen wird, bevor die Komponente gemountet wird.

Dies würde es viel einfacher machen, über den Kontext nachzudenken. Wir können den dritten Parameter aus shouldComponentUpdate entfernen, da der Kontext nie aktualisiert wird.

Falls Sie Aktualisierungen vom Kontexteigentümer benötigen (z. B. wenn der React-Router möchte, dass activeRoutes für die Verwendung in ActiveState mixin cc @mjackson von oben nach unten übergeben wird), hindert Sie nichts daran, { addChangeListener, removeChangeListener, getActiveRoutes } in context . Nachkommen können jetzt Änderungen abonnieren und in state .

Ist das eine vernünftige Lösung?

Ich denke, die Schlüsselfrage ist:

In welchen Szenarien wird _data_ durch context , anstatt Daten durch props leiten oder ein Ereignis auszulösen, damit eine Komponente ihre eigenen setState aufruft.

Ich habe den Kontext verwendet, um Objektreferenzen ziemlich glücklich weiterzugeben, da ich immer nur auf diese Objekte schreibe, nicht lese. z.B. this.context.triggerAction("something")

Der Anwendungsfall für den Kontext war für Parameter, die für einen potenziell großen Teilbaum gelten, die Sie jedoch nicht durch allgemeinere Containerknoten leiten möchten. Ein Beispiel könnte ein Farbthema sein, bei dem eine große Anzahl von Knoten möglicherweise abhört, ob die Hintergrundfarbe weiß oder schwarz sein soll. Sie möchten diese nicht überall als Parameter weitergeben.

Wenn sich getChildContext () eher wie getInitialState () verhält, wird das Problem nicht gelöst, da Sie jederzeit einen übergeordneten Knoten, der einen bestimmten Kontextwert bereitstellt, durch einen anderen übergeordneten Knoten ersetzen können, der einen anderen Kontextwert bereitstellt. Für den betroffenen Teilbaum ist dies nicht von der Mutation des Werts der Kontextvariablen zu unterscheiden.

Ich denke, wir können eine Lösung finden, die verhindert, dass Benutzer Änderungs-Listener verkabeln.

Ich denke, @andreypopp kann richtig sein. Zumindest müssen wir shouldComponentUpdate eine Möglichkeit bieten, um festzustellen, ob sich etwas im Kontext geändert hat, damit es entscheiden kann, immer true zurückzugeben, wenn sich die Kontextvariable geändert hat.

Ich werde später heute mit @sebmarkbage chatten und sehen, was er denkt.

Wenn sich getChildContext () eher wie getInitialState () verhält, wird das Problem nicht gelöst, da Sie jederzeit einen übergeordneten Knoten, der einen bestimmten Kontextwert bereitstellt, durch einen anderen übergeordneten Knoten ersetzen können, der einen anderen Kontextwert bereitstellt. Für den betroffenen Teilbaum ist dies nicht von der Mutation des Werts der Kontextvariablen zu unterscheiden.

Autsch. Du hast vollkommen recht, das habe ich nicht bedacht.

@jsfb Bei einem zweiten Gedanken verstehe ich Ihren Kommentar nicht ganz. Wenn Sie einen übergeordneten Knoten ersetzen, wird der gesamte untergeordnete Teilbaum trotzdem erneut bereitgestellt, nicht wahr?

Derzeit wird es zwar erneut bereitgestellt, dies ist jedoch teilweise ein Implementierungsdetail. Sie können sich vorstellen (und wir haben die Implementierungen und Auswirkungen davon diskutiert), Teilbäume zu reparieren, ohne den Knotenstatus zu verlieren.

@sebmarkbage und ich

  • shouldComponentUpdate ist eine komplexe Escape-Luke, deren Implementierung ein solides Verständnis der Vorgänge unter der Komponente erfordert. Aus diesem Grund ist es nicht unangemessen anzunehmen, dass Sie wissen, welche Kontextvariablen verwendet werden, da Sie bereits wissen müssen, welche Eigenschaften verwendet werden und wie sie verwendet werden.
  • Diese Änderung macht die Situation wahrscheinlich nicht viel schlimmer als sie bereits war, und es ist eine Verbesserung gegenüber der Verwendung der alten "Eigentümer" -Beziehung. Daher ist dieses Problem immer noch eine Diskussion wert, aber wahrscheinlich nicht blockierend.
  • Kontexte sind zu diesem Zeitpunkt ein komplexes Merkmal / Experiment, und wir müssen wahrscheinlich mehr nachdenken, bevor wir sie offiziell unterstützen / dokumentieren. Wenn wir mehr erfahren, werden wir dieses Thema wahrscheinlich weiter durchlaufen.

@sebmarkbage , lass es mich wissen, wenn ich etwas verpasst habe!

Gute Diskussionen! Danke für all das Feedback!

Vielen Dank, dass Sie sich die Zeit genommen haben, dies zu besprechen!

Es ist nicht unangemessen anzunehmen, dass Sie wissen, welche Kontextvariablen verwendet werden, da Sie bereits wissen müssen, welche Eigenschaften verwendet werden und wie sie verwendet werden.

Wenn ein Top-Level - Feed Komponente implementiert shouldComponentUpdate mithilfe von PureRenderMixin vermeiden , zusätzliches Updates, bedeutet es nicht , dass Feed , dass irgendwo im Innern weiß es ist ein Cell , der zufällig ein Link , das vom Kontext des Routers abhängt.

Dies wird noch schlimmer, wenn Frameworks (wie das beliebteste React-Routing-Framework ) den Kontext verwenden und Sie sich dessen möglicherweise nicht einmal bewusst sind. Irgendwo gibt es Apps, die den aktiven Verbindungsstatus nicht ändern, weil jemand Komponenten der obersten Ebene optimiert hat und buchstäblich keine Ahnung hatte, dass er die entsprechenden contextTypes deklarieren musste, um sogar nextContext in shouldComponentUpdate .

Es gibt keine Möglichkeit, dass Komponenten alle möglichen Nachkommen kennen. Wenn ich eine kontextabhängige Komponente an eine beliebige Stelle innerhalb einer PureRenderMixin -fähigen Komponente verschiebe, wird sie grundsätzlich unterbrochen, jedoch sehr subtil. Wenn Sie den Kontext verwenden, besteht die einzige Möglichkeit, diesen subtilen Bruch nicht verwandter Komponenten zu vermeiden, darin, niemals shouldComponentUpdate zu implementieren, was gegen das

Einige Frameworks über React, wie @swannodettes Om, machen PureRenderMixin -ish shouldComponentUpdate _default_, es ist seltsam, sie so abzuschneiden. Das bedeutet für sie kontextbrechende shouldComponentUpdate in jeder Komponente.

Ich bin damit einverstanden, dass dies Ihre Arbeit nicht blockiert, aber ich kann nicht zustimmen, dass die aktuelle Situation zufriedenstellend ist, wenn der Kontext überhaupt verwendet werden soll. Es ist nicht nur schwierig, shouldComponentUpdate jetzt mit Kontext zu implementieren - es ist absolut unmöglich, wenn wir nicht davon ausgehen, dass jede Komponente immer weiß, wie hoch ihre children könnten.

Beliebte Bibliotheken sind bereits stark vom Kontext abhängig. Ich verstehe, dass es keine unterstützte Funktion ist, aber meiner Meinung nach muss es entweder mindestens möglich sein, damit es mit shouldComponentUpdate funktioniert, oder es sollte deaktiviert werden, und Bibliotheken sollten gezwungen werden, es nicht zu verwenden, weil es ist subtil gebrochen.

Dies war von Anfang an bei eingeführten Kontexten bekannt. Wir müssen in der Lage sein, undokumentierte und nicht unterstützte Features als experimentelle Features zu haben, um sie durchlaufen zu können, um Sonderfälle zu finden. Wenn wir beispielsweise keine Kontexte hätten, hätten wir nicht gewusst, dass sie geändert werden müssen, um auf Containern anstatt auf Eigentümern zu basieren. Ein subtiler Bruch ist Teil des Vertrags über die Verwendung von undokumentierten Funktionen.

Ich denke, wir müssen shouldComponentUpdate wenn es irgendwo im übergeordneten Baum einen neuen Kontext gibt. Möglicherweise mit einem shouldUpdateChildContext oder etwas, das bestimmt, ob der gesamte Teilbaum abgeglichen werden muss.

@sebmarkbage

Ich nehme an, das könnte funktionieren, weil sich der Kontext kaum oft ändern soll.
In diesem Fall benötigt shouldComponentUpdate keinen dritten Parameter, oder?

Richtig, es war immer irgendwie nutzlos.

Ich denke, wir müssen shouldComponentUpdate umgehen, wenn irgendwo im übergeordneten Baum ein neuer Kontext vorhanden ist.

Das macht sehr viel Sinn. Kontextänderungen im übergeordneten Element müssen falsche shouldComponentUpdate s im Teilbaum überschreiben.

Aber es stellt sich die Frage: Woher wissen wir, wann sich der Kontext ändert? Für Staat und Requisiten haben wir setState und render / setProps . Es scheint, als müssten wir irgendwo eine Kopie des aktuellen Kontexts aufbewahren und sie mit dem Ergebnis von getChildContext wenn render aufgerufen wird.

Ich verwende Kontexte zum Verwalten von Geschäftsreferenzen. Ich mag das, weil ich explizit definieren kann, welche Geschäfte benötigt werden
1) Ich kann Beispieldaten nur in den Filialen verspotten, in denen ich eine Komponente testen muss, ohne mein größeres Framework instanziieren zu müssen
2) Übergeben Sie einfache schreibgeschützte Speicher mit den bekannten Daten für das serverseitige Rendern, was den serverseitigen Fall vereinfacht, da der Großteil des Anwendungscodes nicht verwendet wird.

Wenn Sie sich also ein Beispiel ansehen,

Bei meiner Implementierung wird auf Speicherdaten nur über Getter zugegriffen. Meine Elemente speichern keine Speicherdaten. Ich möchte eine einzige Version der Wahrheit. Als einfaches nicht asynchrones Beispiel hat mein render () normalerweise so etwas wie

var peopleStore = this.context.peopleStore;
var person = peopleStore.get(this.props.personId);
return <div>person.fullName</div>;

Komponenten hängen Listener an Geschäfte an. Ich habe mich noch nicht vollständig für die Granularität entschieden. Alle Geschäfte haben ein onChange-Ereignis. Ich habe mich jedoch nicht für zwei andere Dinge entschieden:
1) Listener für einzelne Eigenschaftsänderungen
2) wenn Geschäfte Geschäfte enthalten sollten

Wenn in diesem Beispiel "people" "fb users" war, handelt es sich um einen großen komplexen asynchronen Speicher. Sollte ich die Geschäftsstruktur für einen PersonStore wiederverwenden? Eine für die allgemeine Sammlung (getFriends, getPerson usw.), aber viele eindeutige Instanzen des Personenspeichertyps für eine einzelne Person.

In meinem Beispiel benötigt meine Komponente den People-Speicher als Kontextparameter. Anschließend wird die personId-Requisite verwendet, um einen bestimmten Personenspeicher zu identifizieren und zu abonnieren.

Nun einige dynamische Änderungen einführen. Angenommen, der aktuell angemeldete Benutzer meldet sich ab und jemand anderes meldet sich an. Wenn Sie die Tatsache ignorieren, dass Sie die Seite in einer realen Anwendung wahrscheinlich umleiten / aktualisieren würden, wie würde dieses Element aktualisiert? Nehmen wir auch an, dass sich dieses Element noch auf der Seite befindet und nicht nur zerstört wird.

Ich würde erwarten, dass die Anwendungslogik zuerst den vorhandenen People Store entfernt / zerstört. Dafür muss meine Komponente aufhören, auf Updates zu warten. Dafür würde ich eine ReactClass-API empfehlen. Z.B,

onContextChange (prev, new)

Das Element kann dann die beiden peopleStore-Instanzen vergleichen. Ignorieren wir öffentliche Daten und nehmen an, dass der neue PeopleStore null ist. Das Element würde sich vom vorherigen Store abmelden und ein render () auslösen. Das Rendern würde dann eine Nachricht vom Typ "Benutzer unbekannt" anzeigen.

Wenn sich der Benutzer als ein anderer Benutzer anmeldet, wird ein neuer Speicher erstellt, das Element wird erneut angehängt und render () erledigt seine Aufgabe mit neuen Daten.

Hinter den Kulissen konnte "this.render ()" nicht synchron sein. Damit eines meiner Designs / Beispiele überhaupt Sinn ergibt, müssen die render () -Aufrufe vom Framework gesammelt und gestapelt werden.

Im Gegensatz zu Requisiten liegt das Abhören von Geschäften außerhalb der Kernrolle von React bei der Verwaltung des Renderings. Aus diesem Grund denke ich nicht, dass eine Kontextänderung in shouldComponentUpdate () involviert sein sollte. Und trotz meines Beispiels, das das Ändern von Kontextwerten (ein neues Geschäftsobjekt) beinhaltet, denke ich nicht, dass Geschäfte in ihrer übergeordneten Natur unveränderlich sein werden. Ich denke, dass ein typisches Flussmittelanwendungsdesign mit einem Abonnentenmodell, Rückrufen für Async usw. funktioniert. Das Basisspeicherobjekt wird normalerweise für die Lebensdauer der Anwendung leben.

Das ist ziemlich interessant. (Siehe Referenzen oben.)

Ja @gaearon .

Ich möchte Lokalisierungsdaten im Kontext übergeben und dem Benutzer ermöglichen, seine Sprache zur Laufzeit zu ändern, damit sich alle Komponenten mit den aktualisierten Übersetzungen, Währungen, Datumsformatierungen neu rendern können ... Soweit ich weiß, scheint dies vorerst schwer zu machen.

Siehe auch
https://github.com/yahoo/react-intl/issues/58
https://github.com/facebook/react/issues/3038

@slorber Ich habe auch ein benutzerdefiniertes PureRenderMixin , es könnte eindeutig mit strengeren shouldComponentUpdate s in der Mitte kaputt gehen , wie

@gpbl überprüfe das:
https://github.com/facebook/react/issues/3038#issuecomment -76449195

Es scheint mir, dass wir, falls wir die gesamte App im Falle einer Kontextänderung von oben neu rendern möchten, den Knoten im selben Ereignisschleifen-Tick aushängen und wieder einhängen können, und dass dies (ständig) keine flackernden Effekte erzeugt. Dies ist eine perfekte Problemumgehung für den Umgang mit Änderungen der Benutzersprache.

@slorber Das Aufrufen von unmountComponentAtNode führt zu einer schlechten Benutzererfahrung, da dadurch der gesamte lokale Status verloren geht.

Ich denke, es ist Zeit, den Kontext als offizielles Merkmal zu deklarieren. Die Nichtverwendung des Kontexts ist für die meisten Benutzer keine Option, da der Router und ReactIntl ​​ihn verwenden.

@cody et al., Um es klar zu

@cody Dies ist kein Problem, um den lokalen Status für mich zu verlieren, da ich keinen lokalen Status habe und meine gesamte App den unveränderlichen Status ausschließlich außerhalb von React verwaltet :) Überprüfen Sie das Video, das ich für das Framework erstellt habe: https: / /github.com/stample/atom-react

Ich denke, wir müssen shouldComponentUpdate umgehen, wenn irgendwo im übergeordneten Baum ein neuer Kontext vorhanden ist. Möglicherweise mit einem shouldUpdateChildContext oder etwas, das bestimmt, ob der gesamte Teilbaum abgeglichen werden muss.

Ja, shouldUpdateChildContext macht es schön und symmetrisch.

Gibt es eine Chance, dass dies in 0,14 geht?

Wenn Sie eine schöne API entwerfen und implementieren. :) :)

Es sollte eine Möglichkeit enthalten, festzustellen, dass nur die untergeordneten Elemente, die tatsächlich geänderte Kontextschlüssel abhören, tatsächlich aktualisiert werden. Anstelle von allem.

Es ist unwahrscheinlich, dass allein unser Vollzeit-Kernteam Zeit dafür hat. :(

Am 9. April 2015, um 13:32 Uhr, schrieb Dan Abramov [email protected] :

Gibt es eine Chance, dass dies in 0,14 geht?

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an.

Klar, ich werde mal schauen!

Sorry Leute, ich finde immer noch keine Zeit, daran zu arbeiten. Nach der bevorstehenden Veröffentlichung von React DnD 1.0 bin ich mit der Vorbereitung der Konferenz beschäftigt, daher ist es sehr unwahrscheinlich, dass ich bald daran arbeiten kann. :-(

@gaearon Hast du Pläne, dir das bald anzuschauen? Ich bin daran interessiert, mich darauf einzulassen, da dies anscheinend eines der letzten Elemente ist, um den Kontext vollständig nutzbar zu machen. (Vielleicht fehlt mir noch etwas?)

Ich habe mich gefragt, ob die folgende Aussage wahr ist oder nicht:

Der untergeordnete Kontext einer Komponente sollte immer eine Reduzierung der Statusrequisiten und des Kontexts sein

Dies stellt sicher, dass wir die Daten innerhalb des bekannten Lebenszyklus zuverlässig fließen lassen können. Dies ähnelt der Aussage, dass "das Rendern einer Komponente immer eine Reduzierung von Status und Requisiten ist".

So stelle ich es mir derzeit vor - wäre nur gut zu sehen, ob ich völlig vom Kurs abgekommen bin!:

shouldUpdateChildContext bestimmt, ob der Unterbaum mit neuem Kontext aktualisiert werden soll. Dieser Teil des Lebenszyklus wird durch eine Änderung der Statusrequisiten oder des Kontexts der Komponente ausgelöst.

shouldComponentUpdate verbleibt bei 3 Parametern, sodass eine Komponente im oben genannten Unterbaum entscheiden kann, ob sie tatsächlich etwas aktualisieren soll (Hinweis: Die Rückgabe von false hier wirkt sich NICHT auf den Kontext aus, der den Unterbaum weiter hinunterfließt).

Eine andere Sache, die ich mich hier frage, ist, ob es eine andere neue Lebenszyklusmethode geben sollte componentWillReceiveContext ? - Wird zu einem ähnlichen Zeitpunkt wie componentWillReceiveProps aufgerufen, befindet sich jedoch innerhalb des Kontextdatenflusses. Hier können beide Methoden "gleichzeitig" aufgerufen werden.

@ Chrisui

Bitte spring drauf! Ich bin im Moment zu beschäftigt mit anderen Sachen.

Dies stellt sicher, dass wir die Daten innerhalb des bekannten Lebenszyklus zuverlässig fließen lassen können.

Denken Sie auch daran, dass es einen observe Hook gibt, der möglicherweise in 0.14 implementiert wird oder nicht . Was bedeutet, dass möglicherweise data / observed / abgeleitet wird oder nicht, wie auch immer es heißt. Aber es ist wahrscheinlich für Ihre Aufgabe vorerst irrelevant.

Ich habe einen ersten Entwurf davon auf examples/context/index.html läuft

Ich habe eine einfache Beziehung zwischen "Kontext-Eltern" und "Kontext-Kind" eingerichtet. Ein "übergeordnetes Kontext" ist eine Komponente, die den untergeordneten Kontext ändern kann (dh childContextTypes implementiert), und ein "untergeordnetes Kontext" ist eine Komponente, die vom Kontext abhängt oder den untergeordneten Kontext ändert (dh contextTypes implementiert) childContextTypes ). Diese Beziehung wird eingerichtet, wenn Komponenten derzeit bereitgestellt werden.

Wenn der Lebenszyklus der Komponentenaktualisierung ins Spiel kommt, gibt es zwei Fälle:

  1. Wenn die Komponente sagt, dass sie nicht aktualisiert werden soll (d. H. shouldComponentUpdate() === false ), prüfen wir, ob der untergeordnete Kontext mit shouldUpdateChildContext aktualisiert werden soll. Wenn dies true (Standard) ist, wird diese untergeordnete Komponente in den Update-Lebenszyklus überführt, wenn diese Komponente unmittelbare "Kontext-Kinder" hat. Dies wird so lange fortgesetzt, bis wir das Ende des Kontextbaums erreicht haben oder auf shouldUpdateChildContext() === false stoßen
  2. Wenn eine Komponente sagt, dass sie aktualisiert werden soll, ist nichts anders. In diesem Fall erfolgt keine seitliche Kanalisierung von Kontextdaten, und der normale Fluss wird fortgesetzt, bis eine Komponente erneut auf Fall 1 trifft.

Zweifellos habe ich einige wichtige Dinge verpasst. Wenn also jemand einen kurzen Blick darauf werfen könnte, wäre das großartig. Begrüßende Kommentare zur Architektur des Kontexts sowie zum tatsächlichen Schreiben des Codes, der in die React-Codebasis passt! :) :)

Betrachten Sie für ein kurzes Pseudo-Beispiel Folgendes:

<Root num={1}>  // Gives context {num: this.props.num}
  <Mid>         // Ignores any context and shouldComponentUpdate() === false
    <Child>     // Requires context {num: propTypes.number}

Beim ersten Rendern (Montieren) ist alles so, wie wir es erwarten. (Impl. Detail: Eine Eltern / Kind-Beziehung wird zwischen Root & Child )

Wir aktualisieren Root , um den num Prop-Wert von 2

<Root num={2} />

Mid implementiert auch eine shouldComponentUpdate() -Methode, die false zurückgibt, da es sich bei der render() -Methode nicht um context.num und sich nichts anderes geändert hat - Warum sollte es aktualisiert werden?

Dies ist der Punkt, an dem in alten Fällen die Child -Komponente den neuen Kontext niemals sehen würde, da wir aus dem Update im Baum herausgekommen sind.

Mit diesen Änderungen suchen wir nun nach einer shouldUpdateChildContext() -Methode für Mid der (wir haben die Standardaktionen in unseren Beispielen ausgeführt) eine einfache Gleichheitsprüfung durchgeführt werden kann, um festzustellen, ob Kontextwerte vorhanden sind geändert. Hinweis: Die Signatur von shouldUpdateChildContext() ist derzeit identisch mit shouldComponentUpdate() .

Wenn shouldUpdateChildContext() true (dies ist standardmäßig der Fall), aktualisieren wir jedes unserer nächsten 'Kontextkinder' (im Beispielfall nur Child ), wenn zuvor Beziehungen eingerichtet wurden . Dadurch wird diese Komponente in den Aktualisierungslebenszyklus versetzt (im Gegensatz zu den zuvor erwähnten Diskussionen wird dies wie üblich shouldComponentUpdate() mit dem _new_-Kontext als drittem Parameter aufrufen, da dies der Komponente eine detaillierte Kontrolle darüber gibt, wann sie möchte aktualisieren).

Hoffentlich erklärt das den Prozess gut genug!

@Chrisui Cool, toll, einige Fortschritte zu sehen! Wir sollten auch eine Reihe von Komponententests hinzufügen, um die Grundfunktionen der Sanitätsprüfung zu überprüfen. Zumindest:

  • Überprüfen Sie untergeordnete Aktualisierungen, auch wenn übergeordnete shouldComponentUpdate() false zurückgeben.
  • Überprüfen Sie untergeordnete Aktualisierungen, auch wenn übergeordnete shouldUpdateChildContext() false zurückgeben.
  • Überprüfen Sie, ob das untergeordnete Element nicht aktualisiert wird, wenn die Komponente shouldUpdateChildContext() false zurückgibt.

Wenn dies unterstützte Funktionen sind, möchten wir möglicherweise Folgendes überprüfen:

  • Stellen Sie sicher, dass shouldUpdateChildContext() nicht aufgerufen wird, wenn ein Großelternteil seinen bereitgestellten Kontext ändert, ein direkter Elternteil diesen Kontext jedoch überschreibt und sich nicht ändert.
  • Stellen Sie sicher, dass shouldUpdateChildContext() nicht aufgerufen wird, wenn die Komponente nicht aus der geänderten Kontextvariablen liest.
  • Stellen Sie sicher, dass bei einer Änderung einer Requisite und einer Kontextvariablen nicht zweimal aktualisiert / gerendert wird.

cc: @sebmarkbage für Feedback zum API-Design / zur API-Idee.

@Chrisui Das klingt bisher fantastisch, danke, dass du diese Arbeit gemacht hast.

@jimfb Wird definitiv anfangen, einige Unit-Tests durchzuführen - wollte nur etwas zum Spielen haben und bestätigen, wie wir alle denken, dass es vorher funktionieren sollte!

In Bezug auf die zweite Gruppe von Fällen sind dies Bereiche, die ich zugunsten eines einfacheren Designs vermieden habe. Zum Beispiel wurde vorgeschlagen, dass wir beobachten, welche bestimmten Kontextschlüssel geändert werden, und nur diese aktualisieren. Ich bin der Meinung, dass dies die Eltern-Kind-Beziehung derzeit zu komplex macht und weniger Symmetrie mit dem Fluss anderer Daten durch React aufweist. Zum Beispiel (ich kann sehr naiv sein und dies übersehen) vergleichen wir das Ändern von Requisiten nicht intern und überlassen es dem Benutzer, explizit zu definieren, ob zwei separate Datenquellen (dh aktuelle und nächste Requisiten) ein Update verursachen sollen oder einfach sein sollen ignoriert.

Es wäre gut, mehr Diskussionen über dieses spezifische Detail zu sehen, wenn die Leute der festen Überzeugung sind, dass es umgesetzt werden sollte.

Was eine einfache Implementierung betrifft, wenn wir damit fortfahren möchten, würden Sie wahrscheinlich die childContextTypes der Komponenten weiter zusammenführen, während Sie den Baum aktualisiert haben, um ihn mit contextTypes zu vergleichen und eine gültige zu finden Ziele aktualisieren. Oder vielleicht können wir auf dem Berg ein bisschen Magie üben und die Eltern-Kind-Beziehung nur zwischen dem Kind und dem nächsten Elternteil mit einem passenden untergeordneten Kontextschlüssel erstellen. Ich habe das Gefühl, dass Letzteres es später schwierig machen würde, die Update-Reihenfolge zu verwalten, wenn nicht sogar unmöglich.

Ich werde einen schnellen Hack haben und versuchen, dies jetzt zu verspotten!

Eigentlich in Bezug auf meinen letzten Vorschlag für den spezifischen Kontext Schlüsselvergleich Teil der Funktionalität könnte es nicht sein , wie heikel das Update , um die Aufrechterhaltung , wenn wir nur das gleiches folgen durch montieren , Logik sortiert bereits verwendet!

@Chrisui @sebmarkbage Mein Anliegen (es sei denn, ich vergesse / vermisse etwas Offensichtliches) ist, dass jedes Mal, wenn ein Kontextanbieter erneut rendert, alle (möglicherweise Hunderte) untergeordneten Komponenten, die von der bereitgestellten Kontextvariablen abhängen, dazu führen erneut rendern, auch wenn sich die Kontextvariable nicht geändert hat. Meine Intuition ist, dass wir eine Kontextwiedergabe nur dann auslösen sollten, wenn wir einen Hinweis darauf haben, dass ein neuer Wert verfügbar ist (z. B. wenn ein neuer Wert nicht dreifach gleich dem alten Wert ist oder ein Flag / eine Benachrichtigung).

Tatsächlich sieht diese Implementierung wie ein Abonnementsystem aus. Da context effektiv ein globaler Bereich ist, besteht die richtige Lösung möglicherweise darin, dass Komponenten seitwärts Kontextvariablen abonnieren müssen, wodurch wir denselben Mechanismus für den Kontext verwenden können, den wir für alle anderen globalen Abonnements verwenden werden Daten. Siehe https://github.com/facebook/react/issues/3398 , https://github.com/facebook/react/issues/3858 und https://github.com/facebook/react/pull/3920 für relevante Information.

Stochern @sebmarkbage. Ich denke, meine neuesten Überlegungen sprechen für die Verwendung unserer seitlichen Datenladelösung, um dieses Problem zu lösen und die API-Oberfläche zu vermeiden. Ich bin neugierig auf deine Gedanken.

@jimfb Sie haben Recht damit, dass Hunderte von Komponenten neu gerendert werden, aber ich bin mir nicht sicher, ob dies anders ist als jetzt, wenn Sie eine Statusänderung auslösen und shouldComponentUpdate implementieren müssen, um zu verhindern, dass ein Massenupdate auftritt.

Ich stimme jedoch zu, dass dieser Kontext nur wie ein bestimmter Strom von Seitendaten aussieht. Wenn diese Vorschläge den Kaskadeneffekt des Kontexts haben können, sehe ich keinen Grund, sich wirklich auf den Kontext zu entwickeln, wie er jetzt ist. Mal sehen, was andere denken!

Aus Interesse habe ich meinem Entwurf Funktionen hinzugefügt, mit denen nur dann eine Eltern-Kind-Beziehung zwischen Komponenten erstellt werden kann, wenn diese über einen übereinstimmenden Kontext- / Kind-Kontextschlüssel verfügen (verhindert redundante Baumdurchquerung), und eine Aktualisierung verhindert wird, wenn sich der Kontext nicht tatsächlich geändert hat das PureRenderMixin wie immer.

@Chrisui Ich denke, der Unterschied besteht darin, dass ein einzelnes shouldComponentUpdate in einer untergeordneten Komponente große Zweige des Baums abschneiden kann, während es im Kontext viel schwieriger ist, alle Stellen zu finden / zu reparieren, die neu gerendert werden. Selbst wenn Sie aussteigen, wird ein untergeordnetes Element, das von der Kontextvariablen abhängt, erneut gerendert.

Ja, ich bin gespannt, was andere über die Verwendung der seitlichen Datenladelösung denken.

Ich denke, der Kontext ist meistens orthogonal zum seitlichen Laden von Daten

Idealerweise möchte ich eine Möglichkeit haben, ein Handle für meinen Seitwärtsdatenspeicher an Komponenten zu übergeben, die nicht global sind - meistens verwende ich den Kontext.

Die Verwendung eines solchen Kontexts für DI verringert jedoch die Wahrscheinlichkeit, dass er sich im Verlauf der Anwendung ändert.

Am 26. Mai 2015, um 22:10 Uhr, schrieb Jim [email protected] :

@Chrisui Ich denke, der Unterschied besteht darin, dass ein einzelnes shouldComponentUpdate in einer untergeordneten Komponente große Zweige des Baums abschneiden kann, während es im Kontext viel schwieriger ist, alle Stellen zu finden / zu reparieren, die neu gerendert werden. Selbst wenn Sie aussteigen, wird ein untergeordnetes Element, das von der Kontextvariablen abhängt, erneut gerendert.

Ja, ich bin gespannt, was andere über die Verwendung der seitlichen Datenladelösung denken.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an.

Es scheint, als würden alle aktuellen Ratschläge zur Verwendung des Kontexts (über "nicht" hinaus) weiterhin gelten, wenn alles, was sich häufig ändert, ziemlich hohe Kosten für das erneute Rendern verursacht. Eine gezielte Erhöhung dieser Kosten würde nicht unbedingt die Einstellung der Menschen zur Verwendung ändern. Wenn Sie Äste eines Baumes abschneiden möchten, ist dies immer noch recht einfach:

class Mid extends Component {
  shouldComponentUpdate() { return false; }
  shouldUpdateChildContext() { return false; }
  ...
}

Ich bin zufrieden mit der relativen Einfachheit des Kontexts in seiner jetzigen Form + shouldUpdateChildContext. Wenn Sie den zugrunde liegenden Mechanismus so ändern, dass er mit anderen globalen Abonnements übereinstimmt, wird die Benutzerfreundlichkeit hoffentlich nicht zu stark beeinträchtigt.

@ Jimfb
Wie @eplawless bereits betont hat, kann shouldUpdateChildContext verwendet werden, um die Aktualisierung großer Zweige Ihres Baums zu blockieren.

Ich denke, die Kontext-API (mit vorgeschlagenen Änderungen) ist einfach genug, dass wir sie jetzt implementieren und die Interna ändern können, um eine eventuelle Unterstützung beim Laden von Seitendaten zu verwenden, die später unterstützt wird.

Ein weiterer Vorschlag: Wir könnten auch shouldUpdateChildContext () auf false setzen, um dieses neue Verhalten standardmäßig zu verhindern und es zu aktivieren.

Sollten wir diesen API-Vorschlag für 0.14 verfolgen, um zu iterieren und etwas in die Natur zu bringen, um es zu testen (auf die gleiche Weise war das Kontextmerkmal immer - daher nicht dokumentiert)? (Es wäre gut, von den Torwächtern zu hören!)

Ich habe eine PR für Sie geöffnet: https://github.com/facebook/react/pull/3973

Es ist wahrscheinlicher, dass Sie schnelles Feedback zu PRs erhalten, da diese im Vergleich zum Master leichter zu überprüfen sind und Vorrang vor Problemen haben.

Sie wissen, ich glaube, ich habe mich ursprünglich in Bezug auf shouldUpdateChildContext geirrt. Brauchen wir es wirklich Entschuldigung für den Thrash.

Es scheint, als ob shouldComponentUpdate eine Strategie ist, die es in die Verantwortung eines Kindes macht, festzustellen, ob sich etwas geändert hat. Beispielsweise werden möglicherweise nicht alle von Ihnen angegebenen Daten verwendet, um die Ausgabe zu bestimmen. Als Eltern wissen Sie das nicht. Vielleicht ist das genug.

Ich denke auch, dass wir die Eltern-> Kind-Verbindung auf einen festen Satz von Eigenschaften wie Requisiten beschränken möchten. Das heißt, Sie könnten nicht mehrere verschiedene Kontexte aus einer Quelle bereitstellen, wobei ein bestimmtes Kind nur Teile des Kontexts verwendet. Es könnte eine 1: 1-Zuordnung geben. Außerdem habe ich immer gezögert, dass die Kontextblase direkt durch ein Kind fließt, das sie konsumiert, während der legitime Anwendungsfall im Allgemeinen ein "Wurmloch" zwischen zwei Komponenten ist, anstatt nur an alle zu senden (was ein Anti-Muster ist).

Ich werde noch etwas darüber nachdenken und die PR mit Details kommentieren ...

@sebmarkbage Ich könnte auf shouldUpdateChildContext in beide Richtungen gehen. Ich bevorzuge immer die Minimierung der API-Oberfläche. Wir könnten es immer aus der anfänglichen API herauslassen (der Kontext springt einfach durch und initiiert ein erneutes Rendern für jede Komponente, die die Kontextvariable liest). Wenn Leute die zusätzliche Notluke brauchen, fügen wir sie später hinzu.

Ich denke, das größere Problem ist, wie @mjackson betonte, dass wir nicht wissen, wann sich die Kontextvariable geändert hat. Wollen wir wirklich jeden fbt / i18n-Textknoten jedes Mal neu rendern, wenn der i18n ContextProvider erneut gerendert wird? Wir haben noch keine Möglichkeit angegeben, zu sagen: "Keine Sorge, nichts hat sich geändert. Machen Sie sich nicht die Mühe, jedes Textelement auf der Seite neu zu rendern."

Ich bin ein wenig verwirrt über Ihren letzten Absatz; können Sie näher darauf eingehen? Ich gehe davon aus, dass Sie nicht vorschlagen, dass das Folgende ein Anti-Muster des Kontexts ist: Eine i18n-Komponente "sendet" an alle i18n-fähigen untergeordneten Komponenten die vom Benutzer bevorzugte Sprache / Zeitzone / Formatierung / etc.

Kommentiert auf die PR, sollte hier wohl reproduzieren:

@sebmarkbage @Chrisui @jimfb

Betreff: shouldUpdateChildContext , ich denke, es ist eine nützliche Ergänzung. Standardmäßig gibt shouldComponentUpdate true zurück. Daher liegt es in der Verantwortung des Kindes, festzustellen, ob sich etwas geändert hat. Das Implementieren von shouldComponentUpdate bedeutet, dass Sie diese Verantwortung für den Fall übernehmen, dass die Eltern es besser wissen. Das gleiche gilt für shouldUpdateChildContext , es existiert nur, um seinen Kindern als Optimierung die Verantwortung zu entziehen.

Betreff: Nicht für den gesamten Teilbaum senden, ich denke, es gibt zwei gültige Muster. Wurmlöcher, wie Sie in # 2517 vorgeschlagen haben, sind absolut sinnvoll. Wir verwenden dieses Muster zum Gruppieren von Elementen in verschiedenen Subsystemen (wie Fokus und Sprache). Darüber hinaus möchten wir jedoch den Kontext verwenden, um i18n-Informationen an einen ganzen Abschnitt (möglicherweise die Gesamtheit) unserer Anwendung weiterzugeben und ein erneutes Rendern von allem zu erzwingen, was sie verwendet. Diese Art von Sendemuster ist im Vergleich zum Wurmlochmuster wahrscheinlich relativ selten, aber ich glaube, es ist immer noch gültig.

Momentan verwenden wir gelegentlich Mixins für Fokus- und Sprachgruppen aus derselben Quelle. Ich denke, es gibt gültige Anwendungsfälle, um ein 1: 1-Mapping nicht zu erzwingen, obwohl ich den Wunsch nach einer Einschränkung verstehe.

Ich dachte tatsächlich, das "Wurmloch" -Muster sei das Antimuster, und die Sendung sei das erwartete Muster. Der Kontext ist nur dann interessant, wenn es viele Konsumenten der Variablen gibt (dh wenn das explizite Übergeben der Requisite dazu führen würde, dass sie praktisch jeder Komponente hinzugefügt wird und daher viel Boilerplate ist), andernfalls ist es wahrscheinlich besser, sie explizit als zu übergeben Stütze. Aber @sebmarkbage scheint das Gegenteil gesagt zu haben (und er hat normalerweise Recht mit diesem Zeug), also bin ich jetzt verwirrt und möchte eine Klarstellung von ihm bekommen.

@sebmarkbage @eplawless Was ist ein Beispiel für das Wurmlochmuster, das Sie für gültig halten? Was bietet der Elternteil, wie wird es vom Kind benutzt, warum kann es nicht einfach eine Requisite für das Kind sein usw.

<Table>
  <Cell />
  <Cell />
  <FancyCell />
</Table>
class FancyCell {
  render() {
    return <SomeWhatFancyCell>Some content</SomeWhatFancyCell>;
  }
}

class SomeWhatFancyCell {
  render() {
    return <Cell>{this.props.children}</Cell>;
  }
}

Nehmen Sie borderWidth und übergeben Sie es in der Form borderLeft/Top/Right/Bottom an alle Zellen einer Tabelle.

Eine gute Möglichkeit besteht darin, borderWidth an <Table /> , aufzuteilen und den Kontext zu durchlaufen.

Es bietet eine Möglichkeit für etwas, das für eine Zelle gerendert werden könnte, mit ihrem konzeptionellen übergeordneten Element wie eine Tabelle über einen verborgenen Kanal zu kommunizieren. Dies ist bereits das, was DOM-Elemente miteinander tun und wahrscheinlich für das Layout in React erforderlich sind.

Konzentrieren Sie sich nicht zu sehr auf das Konzept eines "Kontexts". Das aktuelle Konzept ist nicht ideal. Vielleicht ist das Ideal so etwas wie zwei verschiedene Konzepte oder ein anderer Kanal?

Plausible alternative API:

class Table {
  render() {
    var w = this.props.borderWidth;
    var borderStyle = { left: w, right: w, top: w, bottom: w };
    return <context key={someSymbol} value={borderStyle}>{this.props.children}</context>
  }
}
class Cell {
  static contextKey = someSymbol;
  render() {
    var borderStyle = this.context;
    ...
  }
}

Nur hier spucken.

Das Spitballing fortsetzen ...

Könnte eine alternative Syntax verwenden ...

<Table borderStyleChannelKey="myKey">
  <Cell borderStyle={myKey} />
  <Cell borderStyle={myKey} />
  <FancyCell borderStyle={myKey} />
</Table>

Jetzt haben Sie den Kommunikationskanal explizit gemacht, jede Möglichkeit von Namenskonflikten usw. vermieden (https://github.com/reactjs/react-future/pull/28).

Ein Symbol hat auch keine Namenskonflikte, aber Sie haben den Kommunikationskanal kostenpflichtig erstellt. Sie haben es durch mehrere Indirektionsebenen geleitet. Jedes Mal, wenn Sie einen anderen Kanal ändern oder hinzufügen müssen, z. B. die Hintergrundfarbe, müssen Sie alles aktualisieren, obwohl der dokumentierte Vertrag (Zelle in einer Tabelle) immer noch derselbe ist.

Übrigens ist die Essenz von React bereits ein versteckter Kommunikationskanal: Zustand. Es geht nicht explizit durch jedes Kind.

Nennen Sie es "styleinfo" oder "celldata" anstelle von "borderStyle" und machen Sie es zu einem Objekt. Für das Hinzufügen von Feldern / Informationen muss der API-Vertrag nicht zwischen Table und Cell .

Der einzige Unterschied zwischen meiner Variante und Ihrer Variante besteht darin, dass (in meiner Variante) das Kind den Wert nur dann "lesen" kann, wenn es explizit als Requisite vom Elternteil übergeben wird. Funktionell ist Ihr letzter Vorschlag buchstäblich identisch mit meinem Vorschlag von https://github.com/reactjs/react-future/pull/28.

Um klar zu sein, ich mag Ihren neuesten Vorschlag wirklich ... also werde ich mich nicht beschweren ... aber er löst ein subtil anderes Problem. Das haben Sie mir im März gesagt, und ich bin letztendlich zu dem Schluss gekommen, dass Sie Recht hatten (zum Beispiel das Rundfunkproblem). Wenn wir das Broadcast-Problem nicht lösen können (so war ich davon überzeugt, dass der Kontext einen gewissen Wert bietet), dann machen wir auf jeden Fall so etwas!

Ein beliebiges Symbol weist Namenskonflikte auf, es sei denn, der Schlüsselname wird immer vom Eigentümer festgelegt. In diesem Fall können Sie ihn auch für den Eigentümer sichtbar machen, da der Eigentümer dem Kind den Schlüsselnamen ohnehin mitteilen muss (andernfalls wie) das Kind weiß, wo es suchen muss?). Indem Sie den Eigentümer zur Beteiligung zwingen, ermutigen Sie Komponenten natürlich, Symbole anstelle von fest codierten Schlüsseln zu verwenden.

Ich meine ein globales Symbol (Großbuchstabe S), das über einen Kanal wie ein gemeinsames Modul orchestriert werden muss. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol

Das Schöne an meiner neuen vorgeschlagenen API ist, dass der Schlüssel kein "Objekteigenschaftsschlüssel" ist, der mit dem Kontrollfluss und der Programmanalyse verwechselt wird. In meinem Vorschlag ist ein erstklassiger dynamischer Wert genau wie der Schlüssel, der den Zustand antreibt.

Oh, ic, @sebmarkbage holt wieder schickes neues Javascript heraus :). Ich hätte wissen müssen; Er ist heute bei einem TC39-Treffen.

OK, Sie haben Recht. Dies wäre sehr effektiv bei der Vermeidung von Namenskonflikten und würde das Broadcast-Problem lösen. Du hast mich verkauft. Ich mag das!

Ich wünschte du wärst heute im Büro! Ich denke, das wäre ein fantastisch faszinierendes Gespräch gewesen!

Persönlich bin ich froh, dass er es nicht war, da dies uns alle auch auf dem Laufenden hält ;-).

Ich mag die Symbolsache wirklich. Die übermäßige Abhängigkeit des Kontexts von den Zeichenfolgenschlüsseln in einem potenziell fremden Baum hatte immer eine schlechte Stimmung.

Wenn beispielsweise zwei Bibliotheken von einem Eltern-Kind-Wurmloch in einer dritten Bibliothek abhängen und zufällig unabhängige Kopien davon verwenden, würden diese Kopien nicht zusammenstoßen oder sich gegenseitig sehen. Sicher, es ist ein seltsames Szenario, das man sich vorstellen kann, aber es scheint konsequenter zu sein.

Um fair zu sein, können Symbole bereits an den aktuellen Objekten arbeiten. Selbst wenn Sie Symbole verwenden, benötigen Sie wahrscheinlich ein Modul über einen potenziell globalen Namespace, z. B. einen Modulnamen der obersten Ebene von npm (obwohl dieser hoffentlich relativ ist).

Es ist nur so, dass die aktuelle API es sehr natürlich macht, einen einfachen Zeichenfolgennamen zu verwenden. Außerdem wird die Laufzeitsemantik mit bestimmten Eigenschaftskennungen kombiniert, wodurch es schwieriger wird, über den erweiterten Modus des Closure Compilers nachzudenken. Auch Schrauben mit VM-Optimierungen.

@sebmarkbage Sie könnten UUIDs (auch bekannt als Symbole) verwenden, ja, aber Ihr neuer Vorschlag ermöglicht die Entscheidung des Schlüssels zur Laufzeit, was bedeutet, dass das übergeordnete Element ihn über Requisiten beeinflussen KÖNNTE (nicht sicher, was Sie davon halten). Trotzdem bin ich mir nicht sicher, wie nützlich das ist, ohne die Werte im jsx-Bereich verfügbar zu machen. Ich muss darüber mehr nachdenken, aber es bietet (vielleicht) eine coole Flexibilität.

Ich bin mir nicht so sicher über die "Wurmloch" -Analogie, das Broadcast-Szenario und das i18n-Beispiel, bei denen ich auf das Problem gestoßen bin, das die vorgeschlagene PR lösen soll.

Mir ist auch nicht ganz klar, wie https://github.com/facebook/react/issues/2517#issuecomment -106597895 hilft - dies scheint mir semantisch äquivalent zu getChildContext und contextTypes zu sein.

Was ich an der shouldUpdateChildContext PR mag, ist, dass nicht alle Kinder in irgendeiner Tiefe aufgelistet werden müssen, um Kontextänderungen zu verbreiten.

AIUI Das ursprüngliche Problem war immer, dass, obwohl shouldComponentUpdate context , die Art des Kontexts bedeutet, dass Komponenten zwischen dem "Sender" und dem "Empfänger" false zurückgeben können, weil sie den Kontext nicht kennen passierte es.

Es scheint, als ob zwei Möglichkeiten, um dieses Problem zu umgehen, darin bestehen, zu verfolgen, welche Komponenten aus einem Kontext über ihnen im Baum lesen, oder einen zusätzlichen Mechanismus zum Weitergeben von Kontextänderungen im Baum zu haben.

Ich möchte meine 2 Cent zu diesem Thema anbieten, nachdem ich für einige Zeit den übergeordneten Kontext in unseren Formularkomponenten verwendet habe . Das Fehlen eines guten Aktualisierungsmechanismus zwischen einem Elternteil und Kindern je nach Kontext führte zur Schaffung eines parallelen, flussähnlichen Mechanismus, bei dem das Formular (das als 'Speicher'-Instanz fungiert) eine listen() -Methode über den Kontext weitergibt. zu dem sich Field Components bei Mount registrieren. Das Formular löst eine Änderung aus, die sich nach unten ausbreitet. Es ist alles sehr fließend und funktioniert in Ordnung, aber es zerstört das Kompositionsmodell von React Components.

Die allgemeine Strategie, eine Komponente in einen HoC zu verpacken, funktioniert hier nicht, da die neue, verpackende Komponente außerhalb des Aktualisierungskanals platziert wird und nicht in der Mitte (wie bei Requisiten). Der große Vorteil des Propellersystems besteht darin, dass die Strömung von oben nach unten verläuft und von allem, was dazwischen steckt, abgefangen werden kann. Wenn hier ein Flussmittel-ähnliches Aktualisierungssystem verwendet wird, muss die Wrapping-Komponente auch das Formular abhören. Dann tritt jedoch ein Problem auf, bei dem stattdessen sowohl die Wrapping-Komponente als auch die ursprüngliche (Wrapee-) Komponente das Form abhören des HoC "übernehmen" und die Daten an die ursprüngliche Komponente weitergeben.

Der Kontext muss eindeutig etwas vor Komponenten "geschützt" werden, die nichts mit dem jeweiligen Kontext zu tun haben. Es muss jedoch einfach sein, in den Datenstrom zu springen, wenn eine Komponente dies wünscht. Im speziellen Fall unserer Formularkomponenten sollten die meisten untergeordneten Elemente der Form von dem Kontext isoliert sein, den sie weitergeben, und nur die Field -Komponente sollte sie erhalten. Wenn ein Verbraucher jedoch Field in einen HoC einwickeln möchte (um Standardeinstellungen festzulegen oder das Verhalten anzupassen), sollte es einen einfachen Weg geben, dies zu tun.

Dies bedeutet, dass jede Kontext-API es wirklich zulassen sollte, dass (autorisierte) Zwischenkomponenten als Reduzierer für diesen Kontext fungieren, genauso wie Komponenten Requisiten reduzieren / zuordnen können, wenn sie an Kinder weitergegeben werden. Der Unterschied besteht darin, dass Requisiten zwar ein "öffentlicher" Dampf sind, der Kontext jedoch ein privater Opt-In-Stream

Ich habe ein bisschen mit der Idee eines abonnementbasierten Kontexts im reaktionsseitigen Kontext gespielt .

Unter der Haube stellen Kontextsender ihren Kindern eine { subscribe(key, cb), getValue(key) } -Schnittstelle zur Verfügung. Ein Abonnement für einen Kontextschlüssel sprudelt in den Kontextbaum, bis er entweder einen Sender dieses bestimmten Schlüssels oder den oberen Rand des Baums erreicht. Rundfunkveranstalter senden Änderungen an Kontextschlüsseln über broadcast({ [key]: newValue, ...otherKeys }) .

@broadcasts([...keys]) und @observes([...keys]) Klassendekorateure binden diese API an React-Komponenten, indem sie context , um den Kontextbaum durch den Komponentenbaum zu verbreiten, und setState , um ein Update in die Warteschlange zu stellen Antwort auf eine Kontextänderung.

Mit ein wenig Boilerplate können Sie eine API einrichten, die dem aktuellen shouldUpdateChildContext -Vorschlag entspricht.

Um Namenskonflikte zu vermeiden, sind Kontextbäume eindeutig . Sie benötigen daher einen Verweis auf einen Kontext, um darin enthaltene Schlüssel zu senden oder zu beobachten.

Ich habe nie den Kontext verwendet, um dynamische Werte weiterzugeben, sondern nur unveränderte Referenzen. Ich sehe hier einige Spielzeugbeispiele dafür, aber hat jemand damit dynamische Werte in Produktions-Apps weitergegeben? Ich finde reale Anwendungsfälle oft aufschlussreicher.

Gibt zum Beispiel für I18n jemand die Schlüssel-Wert-Paare direkt weiter? Oder geben Sie nur einen unveränderlichen Verweis auf ein Objekt weiter, von dem Komponenten Werte erhalten und beobachten können?

Ich war einmal daran interessiert, eine Art Bubble-Up-Ereignis-Dispatching im Kontext zu implementieren. Ich vergesse, wofür genau, aber die Idee war, Eltern zu ermöglichen, Ereignisse von Kindern abzufangen und mehr Kontext hinzuzufügen, damit Kinder es nicht haben Wissen darüber zu haben, wo sie in den globalen Kontext passen.

Um meine 2 Cent hier hinzuzufügen, würde ich es sehr bevorzugen, wenn ein Fix hier so implementiert würde, dass StaticContainer nicht kaputt geht.

Ich denke, es wäre am sinnvollsten, wenn StaticContainer weiterhin so funktionieren würde und alle Updates von den Eltern blockiert würde, sowohl props als auch context .

IMO wäre der beste Weg, dies zu handhaben, shouldComponentUpdate zu erlauben, potenziell Änderungen am von Kindern verwendeten Kontext zu erkennen, anstatt shouldComponentUpdate ignorieren, wenn der Kontext betroffen ist.

@ jedwards1211 Mein Anwendungsfall wäre, Übersetzungsnachrichten innerhalb einer I18nProvider-Komponente abzurufen und dann eine Jed-Instanz (mit gettext-Methoden) weiterzugeben.

Bevor die Nachrichten empfangen werden, geben die gettext-Methoden leere Zeichenfolgen zurück, damit die Benutzeroberfläche optimistisch gerendert werden kann. Wenn der I18nProvider die Nachrichten erhalten hat, würde ich erwarten, dass alles neu gerendert wird, wobei nur die Teile mit übersetzbaren Zeichenfolgen im DOM aktualisiert werden.

Ich muss tatsächlich gettext() -Aufrufe in den Ansichtskomponenten haben, damit ich die Zeichenfolgen extrahieren kann (in diesem Fall mit xgettext).

Kicking dies mit @jquense ein bisschen auf Reactiflux herum; Es wäre nützlich, wenn es ein "Super false " -Konzept für shouldComponentUpdate gäbe.

Während es im Allgemeinen so aussieht, als ob Kontextaktualisierungen über shouldComponentUpdate hinausgehen sollten und false (oder dies würde zumindest die meisten Anwendungsfälle vereinfachen), denke ich auch, dass es Sonderfälle mit zB <StaticContainer> , bei denen ein Elternteil in der Lage sein muss, alle Aktualisierungen seiner Nachkommen zu blockieren, um beispielsweise einen Teilbaum einzufrieren, der gerade übergeht, wie bei <StaticContainer> .

Betrachten Sie beispielsweise https://github.com/rackt/react-router/pull/2454. Ohne die Möglichkeit, Kontextaktualisierungen für untergeordnete Elemente zu blockieren, können wir Aktualisierungen nicht einfach verhindern, um den aktiven Status beim Übergang zu verknüpfen. out route Komponente.

Ich bin damit einverstanden, dass das Verhalten für shouldComponentUpdate , das false zurückgibt, keine Kontextaktualisierungen blockieren sollte, aber es sollte eine Art SUPER_FALSE Sentinel geben, um <StaticContainer> zu behalten

@taion

Es wäre nicht einfach, Aktualisierungen zu verhindern, um den aktiven Status der Komponente für den Übergang zur Route zu verknüpfen.

Ich bin verwirrt, was ist der gewünschte Anwendungsfall? Im Allgemeinen sollte shouldComponentUpdate NICHT verwendet werden, um Aktualisierungen zu blockieren, die zu Änderungen an der Benutzeroberfläche führen würden. Dies ist ein Hinweis, der lediglich als Optimierungsmechanismus gedacht ist, damit Sie unnötige Abstimmungsarbeiten vermeiden können.

@jimfb Sie verwenden jedoch bereits <StaticContainer> , um genau das in Relay zu tun - nämlich um Aktualisierungen beim Laden neuer Daten zu blockieren.

Ich denke, der Anwendungsfall besteht darin, dass der Benutzer auf einen Link klickt und eine neue Route auslöst, die geladen / ausgetauscht werden soll. Währenddessen besteht die Idee darin, alle Interaktionen / Aktualisierungen auf der alten Route zu blockieren, die ausgetauscht werden, wenn die neue fertig geladen ist.

Es hört sich so an, als ob dies derzeit erreicht wird, indem immer false in shouldComponentUpdate während der neue Router-Status geladen wird.

@taion Ich werde mit den Relay-Leuten sprechen und herausfinden, was dort los ist. Bis wir eine endgültige Antwort darauf haben, würde ich nicht empfehlen, diese Technik in Ihrem eigenen Code anzuwenden.

cc @sebmarkbage

@ Jimfb

Wahrscheinlich einfacher für Sie, das zu tun (:

Mein Verständnis der Idee dort ist, dass wir beim Auslösen des Ladens neuer Daten standardmäßig die alten Daten so lange rendern möchten, bis die neuen Daten bereit sind.

Durch die Rückgabe von false von shouldComponentUpdate kann der Relay-Root-Container / Renderer auf einfache Weise einfach weiter rendern, was vorher vorhanden war.

Es blockiert nicht die Interaktionen an sich - es blockiert Upstream-Daten, die Aktualisierungen abrufen, während sie sich in einem vorübergehenden Zustand befinden.

@taion Ja, ich verstehe, was es tut, aber ich bin skeptisch, dass es ein Muster ist, das wir unterstützen wollen. Ich werde mit Sebastian und den Staffelleuten sprechen, aber ich vermute, dass die Antwort "Ja, das war ein Hack, tu das nicht" sein wird.

Normalerweise erreichen Sie dieses Verhalten, indem Sie die alten Werte in den Status versetzen (bis die neuen Werte angezeigt werden / eintreffen) und die alten Werte aus dem Status rendern.

@ Jimfb

Dieses Muster ist nur mit unveränderlichen Daten oder ähnlichem möglich. Wenn Sie (nicht klonbare) statusbehaftete Objekte haben, können Sie diesem Muster möglicherweise nicht folgen. Ich vermute, dass Relay dieses Problem möglicherweise nicht hat, aber die aktuelle Implementierung von React Router hat dieses Problem.

Für allgemeine asynchrone Datenbibliotheken scheint dies ein einigermaßen allgemeines und praktisches Muster zu sein.

@taion Der Ansatz, den Sie in Betracht ziehen, scheint mir noch fehleranfälliger zu sein, INSBESONDERE für den Fall, dass Sie veränderbare Daten haben. Wenn Ihre Daten veränderbar sind, gibt es Rennbedingungen, unter denen Kinder manchmal zufällig aktualisiert werden (aufgrund eines Ereignishandlers, einer Kontextänderung, einer Force-Aktualisierung usw.) und Ihr Code sehr unvorhersehbar wird. Lassen Sie mich mit einigen Leuten synchronisieren und das herausfinden. Pingen Sie mich in ein paar Tagen an, wenn ich das Ergebnis dieser Diskussion nicht veröffentlicht habe.

Ich denke nicht, dass der StaticContainer-Anwendungsfall etwas ist, das durch dieses Muster behandelt werden sollte. Es klingt nach einer instabilen Methode, um einen Effekt aus den Nebenwirkungen einer anderen Funktion zu erzielen. Es wäre viel besser, eine explizite Unterstützung für das zu haben, was tatsächlich angefordert wird.

In diesem Fall scheint es die Anforderung zu sein, dass einige React-Bibliotheken auf niedriger Ebene eine Möglichkeit zum Abfangen des Unmounting, zum Trennen des DOMs, das aus einer Komponente aus dem React-Baum resultiert, zum Trennen der Verbindung und zum Verschieben des Unmount-Lebenszyklus bis zum Abschluss wünschen.

Ich denke, dies ist eine einfache Funktion, die wir mit einer expliziten Schnittstelle unterstützen sollten. Eine Möglichkeit zum Aufheben oder Abfangen des Aufhängens einer Komponente, die die Verbindung unterbricht, ermöglicht es dem Aufrufer, sich eine Weile mit dem zugrunde liegenden Kontext zu beschäftigen, und gibt eine Funktion zurück, die den Aufheben des Bereitstellungs-Livecycle-Flusses beim Aufrufen wieder aufnimmt.

Möglicherweise benötigen wir eine spezielle Form von Fragment oder Platzhalter, die diese API verwenden kann, um React explizit mitzuteilen, dass ein von React getrennter DOM-Knoten an einem bestimmten Ort aufbewahrt werden soll.

Das kann nicht ohne einen ganz besonderen internen Knoten sein. Andererseits die Idee eines speziellen Low-Level-Typs eines Reaktionsknotens, der als Noscript gerendert wird (der typische Reaktionshack für Null-Rückgaben usw.) und erklärt, dass sein nextSibling ein DOM-Knoten ist, der an diesem Ort aufbewahrt werden sollte Andernfalls würde React einen wirklich interessanten Knotentyp ignorieren, der bei der Behebung einer Vielzahl anderer Fehler hilfreich sein könnte.

@taion Ok, ich habe gerade mit einer Person im

Ich kann damit umgehen - ich denke, wir haben ein paar Dinge auf der Seite des React Routers aufgeräumt, damit wir dieses Muster sowieso nicht mehr verwenden können.

Vielen Dank!

Ich möchte noch eine Frage hinzufügen. Ich habe so etwas.

var BlogPosts = React.createClass({
  getChildContext: function() {
    return {
      currentBlogPost: this.props.currentBlogPost,
      currentUser: this.props.currentUser
    };
  },

  childContextTypes: {
    currentBlogPost: React.PropTypes.object,
    currentUser: React.PropTypes.object
  },

  render: function() {
    return <BlogPosts blogPosts={this.props.blogPosts}/>
  }
});

function select(state) {
  const { blogPosts, currentUser, currentBlogId } = state;
  console.log( state.blogs[currentBlogId]); 
  // first time the above is undefined and then blogs get populated and I have the object;
  return { blogPosts, currentUser, currentBlogPost: state.blogs[currentBlogId] };
};

export default connect(select)(BlogPosts);

Jetzt gibt es in der BlogPosts-Komponente BlogPostText, BlogPostImage, PodCast ... je nachdem, ob blogPosts [index] .type Text, Bild oder Audio ist.

in einer der Komponenten habe ich so nach dem Besitzer gesucht,

var BlogPostText = React.createClass({
  canDeleteMemory: function(post, blog, user) {
    return user && (blog.userId == user.id || post.userId == user.id)
  },
  render: function() {
    let isOwner = this.canDeleteMemory(this.context.currentBlogPost, post, this.context.currentUser);
    return isOwner ? <a>Delete</a> : null;
  }
});

dann bekomme ich immer eine Fehlermeldung bei blog.userId, weil das Blog undefiniert ist ... also ändere ich die Bedingung wie folgt: let isOwner = this.context.currentBlogPost && this.canDeleteMemory(this.context.currentBlogPost, post, this.context.currentUser);
aber dann wird das Löschsymbol nie angezeigt ... aber anstatt den contextType zu verwenden, wenn ich die BlogPostText-Komponente mit Redux umhülle, wählen Sie this.props.currentBlogPost aus und verwenden Sie es. Dann funktioniert es einwandfrei.

Eine Änderung des Kontexts löst also kein erneutes Rendern oder ähnliches aus ... oder verwende ich es falsch.

@ aghosh47 Ich denke, eine Kontextänderung löst ein erneutes Rendern aus, aber es ist möglich, dass wir einen Randfall verpasst haben. Wenn Sie eine einfache jsfiddle erstellen könnten, die das Problem demonstriert, würde dies uns bei der Untersuchung helfen.

Bitte posten Sie es auch in einer neuen Ausgabe, da wir Github-Themen so weit wie möglich zum Thema halten möchten.

@jimfb ok, ich werde meinen Code erneut

@jimfb Wo ist die Diskussion über das Reparenting ohne Remounting von Nachkommen? Ich hätte so etwas nie erwartet, daher mache ich mir Sorgen, dass unerwartete Verhaltensänderungen zu meiner App mit zukünftigen Versionen von React führen könnten, wenn dies eingeführt wird. Deshalb möchte ich über diese Diskussion auf dem Laufenden bleiben

Ein weiterer Anwendungsfall für Kontexte, auf die ich gestoßen bin, ist, wenn sich eine Komponente unter mehreren Übergangsgruppen befindet. Zum Beispiel möchte ich in der Lage sein, eine Eingabe zu fokussieren, wenn alle ihre Ahnenübergangsgruppen tatsächlich angezeigt / eingegeben werden, anstatt sie zunächst bereitzustellen. Das Registrieren von Rückrufen für Vorfahren, die über Methoden angezeigt / eingegeben wurden, die über den Kontext weitergegeben wurden, wäre der bequemste Weg, dies zu tun.

@ jedwards1211 Um ehrlich zu sein, ich erinnere mich nicht, wo es besprochen wurde. Es ist durchaus möglich, dass es sich um eine persönliche Diskussion handelte, aber es gibt viele wiederkehrende Diskussionen online, und vielleicht finden Sie sie dort.

Ping @ aghosh47

hahaha

@jimfb Nun , das Problem wurde behoben ... es gab einen Fehler im Code und die Leute, die den Code geschrieben haben, haben catch nicht verwendet, um Fehler aus Versprechungen zu beheben, sodass der Reduzierungsstatus nicht richtig aktualisiert wurde und daher die Änderung nicht erfolgte wird nicht reflektiert.

Wenn sich also die Requisiten im übergeordneten Element ändern, wird der Kontexttyp in den untergeordneten Elementen wiedergegeben ... Danke.

Was ist wirklich falsch daran, Fenster für ein Objekt zu verwenden, in dem diese Globals gespeichert sind?

@cauburtin Dies funktioniert nicht wirklich auf der Serverseite, wo alle erstellten Komponenten denselben globalen Status haben. Der Kontext ist global für alle Komponenten, die in einem einzelnen Baum gerendert werden, jedoch lokal für den Baum selbst. Im Gegensatz zum globalen Staat gelangt es nicht zu anderen Bäumen.

Nebenbei bemerkt, diese Frage hat nichts mit dem Problem zu tun, oder? :zwinkern:

@fatfisz Ich kenne Ihre Anwendungsfälle nicht gut, Sie könnten auch eine Anforderung ('foo') haben, die nur auf dem Client auf eine Fenstereigenschaft verweist

Übrigens habe ich in meiner App ein Objekt in Requisiten aller Komponenten weitergegeben, da der Kontext unklar erscheint und fast alle Komponenten es in meinem Fall verwenden

Vielen Dank

Was ist falsch zu tun:

// context.js
module.exports  = { // sorry for using commonjs, since most of you use import/export I guess
  // some shared variables, initialized and used by components
};

dann erfordern ('./ context.js'); in allen Dateien, die auf Kontextvariablen und -methoden zugreifen müssen

Kapselung wird vielleicht nicht respektiert, aber gut ..

(@gaeron)

@cauburtin Kontext ist nützlich, weil:

1) Im Gegensatz zu Modulexporten kann es sich ändern und ein erneutes Rendern auslösen
2) Es kann vom übergeordneten Element überschrieben werden, was die nützlichste Funktion ist
3) Es muss kein Singleton sein, was für das Rendern von Servern nützlich ist, bei denen Sie Daten isolieren möchten

Einverstanden für 2) und 3)
Zu 1) Ich musste das noch nicht tun, aber Sie könnten auch eine Reaktionsinstanz im Kontext übergeben (ich habe oft gehört, dass es eine schlechte Praxis ist, Sie denken das wahrscheinlich auch, aber es wird sogar in den Dokumenten erwähnt: Erinnern Sie sich daran Sie können auch ganze React-Komponenten in Requisiten übergeben, wenn Sie möchten (wobei Requisiten in diesem Fall stattdessen dieser Singleton-Kontext sein könnten). Für mich ist React die Ansicht, Ereignis-Listener und (eine Top- / Root-) Fernbedienung Dieser gemeinsame Kontext wäre ein gemeinsamer Zugriff auf die Fernbedienung. Unterkomponenten können aktualisiert werden, wenn sich ihre Requisiten ändern, und möglicherweise auf diesen globalen Kontext zugreifen und ihn verwenden. (Insbesondere in diesem Fall kein Fan von Singletons, könnte ich die Weitergabe wiederverwenden Instanz auf Requisiten-Art reagieren, wenn dies nützlich ist, kann dieses "Weitergeben" durch eine gemeinsame Elternklasse oder Komposition mehr oder weniger automatisiert / versteckt werden.

Wenn ich das gut schreibe, merke ich, dass die Idee eines Singleton-Kontexts schlecht ist :)

Was verwirrend ist, ist, dass React-Dokumente nicht viel zur Verwendung von Kontext einladen. Sie sind eigentlich wie optionale Requisiten, bei denen Sie sie erhalten, wenn Sie danach fragen.

Ich implementiere Drawer in Android mit React Native und versuche, eine andere Datei für Drawer Menu Code und Drawer Content Code zu erstellen. Dazu habe ich eine Reaktionskomponente in verschiedenen erstellt. Ich kann alle Arbeiten in dieser Datei ausführen, benötige jedoch eine Schubladenreferenz, um einige Operationen der Schublade in Komponentendateien ausführen zu können. Hier ist mein Code: Wie kann ich die Referenz der Schublade an eine andere Komponentendatei übergeben, um Schubladenmethoden wie openDrawer () zu verwenden?

'use strict';

    var React = require('react-native');
    var { View,
          StyleSheet,
          TouchableHighlight,
          } = React;

    var DrawerLayout = require('react-native-drawer-layout');
    var DrawerScreen = require('./DrawerScreen');
    var DrawerMenu = require('./DrawerMenu');

    var DrawerLayoutExample = React.createClass({

      render: function() {
        var navigationView = (
          <View >
               <DrawerMenu/>
          </View>
        );

        return (
          <DrawerLayout
            onDrawerSlide={(e) => this.setState({drawerSlideOutput: JSON.stringify(e.nativeEvent)})}
            onDrawerStateChanged={(e) => this.setState({drawerStateChangedOutput: JSON.stringify(e)})}
            drawerWidth={200}
            ref={(drawer) => { return this.drawer = drawer  }}
            keyboardDismissMode="on-drag"
            renderNavigationView={() => navigationView}>
            <View style={styles.container}>
 // Here is content component for drawer, need to refer drawer reference
            <DrawerScreen ></DrawerScreen>
            </View>
          </DrawerLayout>
        );
      }
    });

    var styles = StyleSheet.create({
      container: {
        alignItems: 'center',
        justifyContent: 'center',
        flex: 1,
        flexDirection: 'column',
      },
     });

    module.exports = DrawerLayoutExample;

DrawerScreen.js

'use strict';
var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Dimensions,
  Image,
  TouchableHighlight,
  TextInput,
} = React;

var deviceWidth = Dimensions.get('window').width;

var DrawerScreen = React.createClass({


  render: function() {
    return (

        <View style={styles.container}>

          <Text style={styles.welcome}>Content!</Text>

          <TouchableHighlight onPress={() => this.state.openDrawer()}>
            <Text>Open drawer</Text>
          </TouchableHighlight>
          <TextInput style={styles.inputField} />
        </View>
    );
  },

});

var styles = StyleSheet.create({
   container: {
      alignItems: 'center',
      justifyContent: 'center',
      flex: 1,
          flexDirection: 'column',
    },
    inputField: {
      backgroundColor: '#F2F2F2',
      height: 40,
    },
});

Im Moment verwende ich Redux, um Medienabfragen zu abonnieren und zu speichern, sodass Komponenten entscheiden können, auf einem Telefon anders zu rendern.

Dies bedeutet, dass ansonsten reine Komponenten ein Abonnement für den Store benötigen und diese Art von Gerüchen für mich falsch ist, ganz zu schweigen vom Abonnentenmodell von Redux, das zu einer ganzen Reihe von Abonnements für etwas führt, das sich selten ändert.

Ich denke, dass in diesem Fall der Kontext ein viel besserer Ort zum Speichern ist, aber dieses Problem hindert mich daran, dies zu verwenden.

@wmertens das ist etwas, was nur auf componentDidMount gemacht werden muss, oder?

@cauburtin überhaupt nicht, zu jedem Zeitpunkt kann der Benutzer eine andere Sprache wählen, und zu jedem Zeitpunkt kann der Benutzer die Größe des Browsers ändern ...

Zum Ändern der Größe können Sie in componentDidMount warten, für Sprachen würde ich alles neu rendern

Ich ziehe es vor, nicht die gesamte Größenänderungslogik zu wiederholen, wenn ich etwas Cooles wie a habe
redux actioncreator, der die gesamte apo abhört und serverseitig arbeitet
auch…
Ich habe state.responsive.isPhone / isPrerender / screenwidth usw. Auf dem Server,
Ich versende basierend auf User Agent. Ordentlich.

Am Fr, 29. April 2016, 16:52 Uhr Cyril Auburtin [email protected]
schrieb:

Zum Ändern der Größe können Sie in componentDidMount nach Sprachen suchen, die ich verwenden würde
alles neu rendern

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/facebook/react/issues/2517#issuecomment -215742327

Wout.
(auf dem Handy getippt, entschuldigen Sie die Knappheit)

Aber ja, ein einfacher Schlüssel = {lang} am oberen Rand des Baums sollte ausreichen
Sprachen. Nicht so schön für Seiten mit Animationen.

Am Fr, 29. April 2016, 18:07 Uhr Wout Mertens wout. [email protected] schrieb:

Ich ziehe es vor, nicht die gesamte Größenänderungslogik zu wiederholen, wenn ich etwas Cooles wie a habe
redux actioncreator, der die gesamte apo abhört und serverseitig arbeitet
auch…
Ich habe state.responsive.isPhone / isPrerender / screenwidth etc. Auf dem
Server, ich versende basierend auf User Agent. Ordentlich.

Am Fr, 29. April 2016, 16:52 Uhr Cyril Auburtin [email protected]
schrieb:

Zum Ändern der Größe können Sie in componentDidMount nach Sprachen suchen, die ich verwenden würde
alles neu rendern

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/facebook/react/issues/2517#issuecomment -215742327

Wout.
(auf dem Handy getippt, entschuldigen Sie die Knappheit)

Wout.
(auf dem Handy getippt, entschuldigen Sie die Knappheit)

Ich war wie Sie, aber die Größe von Event-Listenern ist eigentlich billig. Ich denke nicht, dass es schlecht ist, viele Listener des gleichen Typs zu haben

Nono, ich bezweifle nicht, dass sie billig sind, es ist nur viel einfacher
this.context.isPhone zu schreiben, als die Größenänderungshandler zu verwalten.

Am Fr, 29. April 2016 um 18:38 Uhr Cyril Auburtin [email protected]
schrieb:

Ich war wie Sie, aber die Größe von Event-Listenern ist eigentlich billig, ich nicht
Ich denke, es ist schlecht, viele Hörer des gleichen Typs zu haben

- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/facebook/react/issues/2517#issuecomment -215797819

Wout.
(auf dem Handy getippt, entschuldigen Sie die Knappheit)

Das Problem ist, dass react-redux ein === ReactElement zurückgibt, um "no update" von der Rendermethode zu signalisieren. Tatsächlich wird diese Verknüpfung jedoch nur getroffen, wenn die Kontexte ebenfalls === sind, was nicht über die Komponente gesteuert werden kann. Ich bin mir jedoch nicht ganz sicher, warum dies so gemacht wird. Vielleicht eine Frage an die React-Redux-Leute.

Informationen zur Arbeit mit Redux finden Sie unter https://github.com/reactjs/react-router/issues/470 und zur weiteren Diskussion in PRs.

Irgendwann werden wir tatsächlich https://github.com/reactjs/react-router/issues/3484 auflösen, um anderen Bibliotheken die Verwendung zu erleichtern, aber es ist schwieriger als ursprünglich angenommen.

Hinweis zur derzeit vom React-Router verwendeten Lösung. Es funktioniert, ist aber ein frustrierender Hack. Es erfordert, dass wir den Kontext direkt mutieren oder dass Benutzer die referenzielle Identität zwischen ihren Kontextobjekten beibehalten.

Beides ist nicht großartig, aber das eigentliche Problem ist, dass der Wurmlochkontext um die Komponentenhierarchie eines der schönsten Teile des React-Komponentenmodells für den Datenfluss zerstört, dh, dass alles in der Mitte (mit Autorität) den Kontext während des Durchlaufs anpassen kann . Wenn der Kontext durch "Out-of-Band" -Komponenten geleitet wird, muss ein anderer Mechanismus zum Zuordnen des Kontexts zu Kindern verwendet werden, oder es muss einfach nicht geschehen. Zugegeben, es ist kein gewöhnlicher Anwendungsfall, aber das beschreibt trotzdem die gesamte Kontextnutzung

Zwischen den beiden Ansätzen ist es zumindest etwas generisch ... das Beste, was wir jetzt tun können, denke ich 😛

yes inded: P, nur mit der Bemerkung, dass es immer noch suboptimal und kein guter Ersatz für die Festlegung der Funktionsweise des Kontexts im Allgemeinen ist

@jquense Konnte die Komponente in der Mitte nicht einfach contextTypes deklarieren, um auf das Kontextobjekt von oben zu warten , und childContextTypes / getChildContext() , um eine modifizierte Kopie dieses Kontexts bereitzustellen seine Nachkommen?

@DarylCantrell- Komponenten müssen sich für jeden anmelden . Es gibt keine Möglichkeit, contextTypes zu deklarieren, um den gesamten Kontext im Großhandel an eine Komponente zu übergeben.

Auf jeden Fall ist mein Punkt, dass das Anpassen von Kontextbits möglich ist und ein großartiges Merkmal des Kontexts, das durch Problemumgehungen für dieses Problem unterbrochen wird

@ 1000hz Sicher, aber er sprach davon, "Teile des Kontexts anzupassen, während er durchläuft". In diesem Szenario müssen Sie nicht den "gesamten" Kontext abrufen, sondern nur den Teil, den Sie überschreiben möchten.

@DarylCantrell Whoops, ich habe deine Absicht falsch verstanden.

Ich habe eine Pull-Anfrage für dieses Problem eingereicht: # 7213

(von Pull-Anfrage)
Ich verwende den Kontext, um Gebietsschema- und Routing-Informationen zu verbreiten. Mein Problem ist, dass einige reine Komponenten das Rendern von Teilbäumen stoppen, wenn sich der Kontext ändert (da reine Komponenten nur den Status und die Requisiten überprüfen). Dies ist ein Problem für viele Menschen gemäß Ausgabe Nr. 2517.

Ich bin der Meinung, dass es besser wäre, den Kontext für Zwischenkomponenten nicht zu maskieren, als ihn zu maskieren, und dass die definierten Kontexttypen nur zur Validierung und nicht zum Filtern verwendet würden. Dies ähnelt der Funktionsweise von propTypes, bei der die angegebenen Eigenschaften überprüft werden, die nicht angegebenen jedoch weiterhin für die Komponente verfügbar sind. Ich habe die Tests aktualisiert, um dieses Update widerzuspiegeln.

Da Komponenten, die nicht am Kontext interessiert sind, normalerweise das Kontextargument nicht verwenden, führt das Nichtmaskieren des Kontexts nicht dazu, dass eine vorhandene Komponente beschädigt wird, es sei denn, diese Komponenten überprüfen explizit, ob der Kontextparameter undefiniert ist. Dies würde jedoch eine Gelegenheit für eine verfeinerte reine Komponente schaffen, beispielsweise eine ContextAwarePureComponent, die Status, Requisiten und Kontext in shouldComponentUpdate vergleicht und es ermöglicht, dass der aktualisierte Kontext erneutes Rendern reiner Komponenten bewirkt.

Ich bin derzeit in der Entwicklung mit diesem Patch, verwende Radium und einige React-Bootstrap-Komponenten und habe keine Probleme festgestellt

Hallo @bvella , ich denke du

@DarylCantrell du hast recht, danke

Ich denke, https://github.com/facebook/react/pull/7213 / https://github.com/facebook/react/pull/7225 ist gut und sollte angepasst werden

Mein Verständnis ist, dass der Kontext wie globale Variablen ist und daher nicht verwendet werden sollte.
Dies bedeutet jedoch nicht, dass ein Benutzer eine Deklaration abgeben muss, bevor er auf globale Variablen zugreifen kann.
Auf globale Variablen sollte immer zugegriffen werden können, unabhängig davon, ob Sie deklarieren oder nicht.

Die contextType-Deklaration sollte genau wie propTypes nur zu Validierungszwecken funktionieren

Denken Sie bitte noch einmal darüber nach, da es uns viel Ärger bereitet
(Ich verwende React Relay und es verbirgt alle Kontextobjekte.)

Ich habe https://github.com/facebook/react/pull/7225#issuecomment -276618328 kommentiert und argumentiert, dass der Kontext einfach alles aktualisieren sollte und sCU nur dazu dient, zu entscheiden, ob diese Komponente neu gerendert werden muss ;; seine Kinder werden trotzdem überprüft.

Das Ausführen von Kontextaktualisierungen auf diese Weise ist kostspielig, aber weniger kostspielig als das erzwungene erneute Rendern des gesamten Baums. Dies ist derzeit die einzige Möglichkeit, Kontextaktualisierungen zu erhalten, um sie auf alle Komponenten zu übertragen. Der Kontext soll sich nicht so sehr ändern. Es gibt bessere Optionen für sich schnell ändernde Updates (z. B. Redux).

Gibt es momentan eine Lösung?

Wie wir aus diesem Thread wissen, ist die bestehende Lösung ziemlich grundlegend kaputt.
Wir können die aktuelle API nicht reparieren, ohne alle Apps langsamer zu machen (was wir vermeiden möchten :-).

Aus diesem Grund schlagen wir eine neue API vor, die dieselben Anwendungsfälle behandelt, jedoch keine Designfehler aufweist. Es ist geplant, dass APIs nebeneinander vorhanden sind und später die alte API auslaufen, wenn Benutzer migriert sind.

Schauen Sie sich die Diskussion an: https://github.com/reactjs/rfcs/pull/2

@acdlite hat gerade eine PR mit der neuen Kontext-API erhalten: https://github.com/facebook/react/pull/11818.

Ich denke, wir können dies als abgeschlossen betrachten. Die neue API hat dieses Problem nicht. Die alte API kann nicht repariert werden, und wir werden sie irgendwann nach dem Upgrade der Hauptbibliotheken auf die neue API ablehnen.

Die neue API wird in einer der nächsten kleineren React 16.x-Versionen verfügbar und dokumentiert sein.

@gaearon Ich

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen