Reagieren Sie auf eine funktionale zustandslose Komponente, PureComponent, Component; Was sind die Unterschiede und wann sollten wir was verwenden?

188

Wir haben erfahren , dass wir ab React v15.3.0 eine neue Basisklasse namens PureComponent haben , die mit dem integrierten PureRenderMixin erweitert werden kann . Was ich verstehe ist, dass unter der Haube ein flacher Vergleich der Requisiten im Inneren verwendet wird shouldComponentUpdate.

Jetzt haben wir drei Möglichkeiten, eine React-Komponente zu definieren:

  1. Funktionale zustandslose Komponente, die keine Klasse erweitert
  2. Eine Komponente, die die PureComponentKlasse erweitert
  3. Eine normale Komponente, die die ComponentKlasse erweitert

Vor einiger Zeit haben wir zustandslose Komponenten als reine Komponenten oder sogar als dumme Komponenten bezeichnet. Anscheinend hat sich die gesamte Definition des Wortes "rein" in React geändert.

Obwohl ich grundlegende Unterschiede zwischen diesen drei verstehe, bin ich mir immer noch nicht sicher, wann ich was wählen soll . Welche Auswirkungen und Kompromisse hat die Leistung?


Update :

Dies ist die Frage, die ich voraussichtlich klären werde:

  • Sollte ich meine einfachen Komponenten aus Gründen der Einfachheit als funktional definieren oder die PureComponentKlasse erweitern (aus Gründen der Leistung)?
  • Ist der Leistungsschub, den ich bekomme, ein echter Kompromiss für die Einfachheit, die ich verloren habe?
  • Würde ich jemals die normale ComponentKlasse erweitern müssen, wenn ich sie immer PureComponentfür eine bessere Leistung verwenden kann?
Yadhu Kiran
quelle

Antworten:

315

Wie entscheiden Sie sich, wie wählen Sie zwischen diesen drei basierend auf dem Zweck / der Größe / den Requisiten / dem Verhalten unserer Komponenten?

Das Erweitern von React.PureComponentoder von React.Componentmit einer benutzerdefinierten shouldComponentUpdateMethode hat Auswirkungen auf die Leistung. Die Verwendung zustandsloser Funktionskomponenten ist eine "architektonische" Wahl und bietet (noch) keine sofort einsatzbereiten Leistungsvorteile.

  • Für einfache Komponenten nur für Präsentationen, die leicht wiederverwendet werden müssen, bevorzugen Sie zustandslose Funktionskomponenten. Auf diese Weise sind Sie sicher, dass sie von der eigentlichen App-Logik entkoppelt sind, dass sie kinderleicht zu testen sind und keine unerwarteten Nebenwirkungen haben. Die Ausnahme ist, wenn Sie aus irgendeinem Grund viele davon haben oder wenn Sie die Rendermethode wirklich optimieren müssen (da Sie dies shouldComponentUpdatefür eine zustandslose Funktionskomponente nicht definieren können ).

  • Erweitern PureComponentSie, wenn Sie wissen, dass Ihre Ausgabe von einfachen Requisiten / Status abhängt ("einfach" bedeutet, dass keine verschachtelten Datenstrukturen vorhanden sind, da PureComponent einen flachen Vergleich durchführt) UND Sie einige Leistungsverbesserungen benötigen / erhalten können.

  • Erweitern Componentund implementieren Sie Ihre eigenen, shouldComponentUpdatewenn Sie Leistungssteigerungen benötigen, indem Sie eine benutzerdefinierte Vergleichslogik zwischen den nächsten / aktuellen Requisiten und dem Status durchführen. Mit lodash # isEqual können Sie beispielsweise schnell einen umfassenden Vergleich durchführen:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }

Wenn Sie Ihre eigenen implementieren shouldComponentUpdateoder erweitern, handelt PureComponentes sich auch um Optimierungen. Wie üblich sollten Sie dies nur dann prüfen, wenn Sie Leistungsprobleme haben ( vermeiden Sie vorzeitige Optimierungen ). Als Faustregel versuche ich immer, diese Optimierungen vorzunehmen, nachdem die Anwendung in einem funktionierenden Zustand ist, wobei die meisten Funktionen bereits implementiert sind. Es ist viel einfacher, sich auf Leistungsprobleme zu konzentrieren, wenn sie tatsächlich im Weg sind.

Mehr Details

Funktionale zustandslose Komponenten:

Diese werden nur mit einer Funktion definiert. Da es für eine zustandslose Komponente keinen internen Status gibt, hängt die Ausgabe (was gerendert wird) nur von den Requisiten ab, die als Eingabe für diese Funktion angegeben wurden.

Vorteile:

  • Einfachste Möglichkeit, eine Komponente in React zu definieren. Wenn Sie keinen Status verwalten müssen, warum sollten Sie sich dann um Klassen und Vererbung kümmern? Einer der Hauptunterschiede zwischen einer Funktion und einer Klasse besteht darin, dass Sie bei der Funktion sicher sind, dass die Ausgabe nur von der Eingabe abhängt (nicht von der Historie der vorherigen Ausführungen).

  • Idealerweise sollten Sie in Ihrer App darauf abzielen, so viele zustandslose Komponenten wie möglich zu haben, da dies normalerweise bedeutet, dass Sie Ihre Logik außerhalb der Ansichtsebene verschoben und in eine Art Redux verschoben haben. Dies bedeutet, dass Sie Ihre reale Logik testen können, ohne etwas rendern zu müssen (viel einfacher zu testen, wiederverwendbarer usw.).

Nachteile:

  • Keine Lebenszyklusmethoden. Sie haben keine Möglichkeit, componentDidMountandere Freunde zu definieren . Normalerweise tun Sie dies innerhalb einer übergeordneten Komponente, die höher in der Hierarchie liegt, damit Sie alle untergeordneten Komponenten in zustandslose verwandeln können.

  • Keine Möglichkeit, manuell zu steuern, wann ein erneutes Rendern erforderlich ist, da Sie nicht definieren können shouldComponentUpdate. Ein erneutes Rendern erfolgt jedes Mal, wenn die Komponente neue Requisiten erhält (keine Möglichkeit zum flachen Vergleich usw.). In Zukunft könnte React zustandslose Komponenten automatisch optimieren. Derzeit können Sie einige Bibliotheken verwenden. Da zustandslose Komponenten nur Funktionen sind, ist dies im Grunde das klassische Problem der "Funktionserinnerung".

  • Referenzen werden nicht unterstützt: https://github.com/facebook/react/issues/4936

Eine Komponente, die die PureComponent-Klasse erweitert VS Eine normale Komponente, die die Component-Klasse erweitert:

Reagieren PureRenderMixinSie, um eine Klasse anzuhängen, die mithilfe der React.createClassSyntax definiert wurde . Das Mixin würde einfach shouldComponentUpdateeinen flachen Vergleich zwischen den nächsten Requisiten und dem nächsten Zustand definieren, um zu überprüfen, ob sich dort etwas geändert hat. Wenn sich nichts ändert, muss kein erneutes Rendern durchgeführt werden.

Wenn Sie die ES6-Syntax verwenden möchten, können Sie keine Mixins verwenden. Aus Bequemlichkeitsgründen hat React eine PureComponentKlasse eingeführt, von der Sie erben können, anstatt sie zu verwenden Component. PureComponentimplementiert nur shouldComponentUpdatein der gleichen Weise wie die PureRendererMixin. Es ist meistens eine bequeme Sache, so dass Sie es nicht selbst implementieren müssen, da ein flacher Vergleich zwischen aktuellem / nächstem Zustand und Requisiten wahrscheinlich das häufigste Szenario ist, das Ihnen einige schnelle Leistungsgewinne bringen kann.

Beispiel:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Wie Sie sehen können, hängt die Ausgabe von props.imageUrlund ab props.username. Wenn Sie in einer übergeordneten Komponente <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />mit denselben Requisiten rendern , wird React renderjedes Mal aufgerufen , selbst wenn die Ausgabe genau gleich wäre. Denken Sie jedoch daran, dass React dom diffing implementiert, sodass das DOM nicht aktualisiert wird. Das Durchführen des Domdifferenzierens kann jedoch teuer sein, sodass es in diesem Szenario eine Verschwendung wäre.

Wenn die UserAvatarKomponente PureComponentstattdessen erweitert wird, wird ein flacher Vergleich durchgeführt. Und weil Requisiten und nextProps gleich sind, renderwerden sie überhaupt nicht aufgerufen.

Anmerkungen zur Definition von "rein" in React:

Im Allgemeinen ist eine "reine Funktion" eine Funktion, die bei gleicher Eingabe immer das gleiche Ergebnis liefert. Die Ausgabe (für React wird dies von der renderMethode zurückgegeben) hängt nicht von der Historie / dem Status ab und hat keine Nebenwirkungen (Operationen, die die "Welt" außerhalb der Funktion verändern).

In React sind zustandslose Komponenten gemäß der obigen Definition nicht unbedingt reine Komponenten, wenn Sie "zustandslos" als eine Komponente bezeichnen, die niemals aufruft this.setStateund nicht verwendet this.state.

Tatsächlich können Sie in a PureComponentimmer noch Nebenwirkungen während der Lebenszyklusmethoden ausführen. Zum Beispiel könnten Sie eine Ajax-Anfrage nach innen senden componentDidMountoder eine DOM-Berechnung durchführen, um die Höhe eines Divs innerhalb dynamisch anzupassen render.

Die Definition "Dumme Komponenten" hat eine "praktischere" Bedeutung (zumindest nach meinem Verständnis): Einer dummen Komponente wird "gesagt", was von einer übergeordneten Komponente über Requisiten zu tun ist, und sie weiß nicht, wie man Dinge macht, sondern verwendet Requisiten Rückrufe stattdessen.

Beispiel eines "smart" AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.expandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

Beispiel eines "dummen" AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

Am Ende würde ich sagen, dass "dumm", "staatenlos" und "rein" ganz unterschiedliche Konzepte sind, die sich manchmal überschneiden können, aber nicht unbedingt, abhängig hauptsächlich von Ihrem Anwendungsfall.

fabio.sussetto
quelle
1
Ich schätze Ihre Antwort und das Wissen, das Sie geteilt haben, sehr. Aber meine eigentliche Frage ist, wann wir was wählen sollen. . Wie soll ich das gleiche Beispiel definieren, das Sie in Ihrer Antwort erwähnt haben? Sollte es sich um eine funktionale zustandslose Komponente handeln (wenn ja, warum?) Oder um eine Erweiterung von PureComponent (warum?) Oder um eine Erweiterung der Component-Klasse (erneut warum?). Wie entscheiden Sie sich, wie wählen Sie zwischen diesen drei basierend auf dem Zweck / der Größe / den Requisiten / dem Verhalten unserer Komponenten?
Yadhu Kiran
1
Kein Problem. Für die funktionale zustandslose Komponente gibt es eine Vor- / Nachteile-Liste, die Sie berücksichtigen können, um zu entscheiden, ob dies gut passt. Beantwortet das Ihren ersten Punkt? Ich werde versuchen, die Auswahlfrage etwas genauer zu beantworten.
fabio.sussetto
2
Funktionskomponenten werden immer neu gerendert, wenn die übergeordnete Komponente aktualisiert wird, auch wenn sie überhaupt nicht verwendet werden props. Beispiel .
AlexM
1
Dies ist eine der umfassendsten Antworten, die ich seit einiger Zeit gelesen habe. Gute Arbeit. Ein Kommentar zum allerersten Satz: Beim Erweitern PureComponentsollten Sie nicht implementieren shouldComponentUpdate(). Sie sollten eine Warnung sehen, wenn Sie dies tatsächlich tun.
jjramos
1
Um echte Leistungssteigerungen zu erzielen, sollten Sie versuchen, PureComponentKomponenten zu verwenden, die über verschachtelte Objekt- / Array-Eigenschaften verfügen. Natürlich muss man sich bewusst sein, was passiert. Wenn ich das richtig verstehe, wenn Sie Requisiten / Status nicht direkt (was React versucht, Sie daran zu hindern, mit Warnungen umzugehen) oder über eine externe Bibliothek mutieren, sollten Sie sie mit Ausnahme PureComponentvon Componentfast überall verwenden können ... mit Ausnahme von sehr einfachen Komponenten, bei denen es tatsächlich schneller sein kann, sie NICHT zu verwenden - siehe news.ycombinator.com/item?id=14418576
Matt Browne
28

Ich bin kein Genie überreagieren, aber nach meinem Verständnis können wir jede Komponente in folgenden Situationen verwenden

  1. Zustandslose Komponente - Dies ist die Komponente, die keinen Lebenszyklus hat. Daher sollten diese Komponenten zum Rendern des Wiederholungselements der übergeordneten Komponente verwendet werden, z. B. zum Rendern der Textliste, in der nur die Informationen angezeigt werden und keine Aktionen ausgeführt werden müssen.

  2. Reine Komponente - Dies sind die Elemente, die einen Lebenszyklus haben und immer das gleiche Ergebnis liefern, wenn ein bestimmter Satz von Requisiten gegeben wird. Diese Komponenten können verwendet werden, wenn eine Ergebnisliste oder bestimmte Objektdaten angezeigt werden, die keine komplexen untergeordneten Elemente enthalten und zur Ausführung von Operationen verwendet werden, die sich nur auf sich selbst auswirken. Eine solche Anzeige einer Liste von Benutzerkarten oder einer Liste von Produktkarten (grundlegende Produktinformationen) und nur eine Aktion, die der Benutzer ausführen kann, ist das Klicken, um die Detailseite anzuzeigen oder dem Warenkorb hinzuzufügen.

  3. Normale Komponenten oder komplexe Komponenten - Ich habe den Begriff komplexe Komponente verwendet, da dies normalerweise Komponenten auf Seitenebene sind und aus vielen untergeordneten Komponenten bestehen. Da sich jedes untergeordnete Element auf seine eigene Weise verhalten kann, können Sie nicht 100% sicher sein, dass dies der Fall ist Rendern Sie das gleiche Ergebnis für den angegebenen Status. Wie gesagt, normalerweise sollten diese als Containerkomponenten verwendet werden

abhirathore2006
quelle
1
Dieser Ansatz könnte funktionieren, aber Sie könnten große Leistungssteigerungen verpassen. Bei der Verwendung von PureComponentKomponenten auf Stammebene und Komponenten in der Nähe der Spitze Ihrer Hierarchie werden normalerweise die größten Leistungssteigerungen erzielt. Natürlich müssen Sie vermeiden, Requisiten zu mutieren und direkt anzugeben, damit reine Komponenten korrekt funktionieren, aber das direkte Mutieren von Objekten ist in React sowieso ein Anti-Pattern.
Matt Browne
5
  • React.Componentist die Standardkomponente "normal". Sie deklarieren sie mit dem classSchlüsselwort und extends React.Component. Stellen Sie sich diese als Klasse vor, mit Lebenszyklusmethoden, Ereignishandlern und anderen Methoden.

  • React.PureComponentist eine React.Component, die shouldComponentUpdate()mit einer Funktion implementiert wird , die einen flachen Vergleich ihrer propsund durchführt state. Sie müssen verwenden, forceUpdate()wenn Sie wissen, dass die Komponente Requisiten oder verschachtelte Statusdaten enthält, die sich geändert haben, und Sie sie erneut rendern möchten. Sie sind also nicht besonders gut, wenn Sie Komponenten zum erneuten Rendern benötigen, wenn Arrays oder Objekte, die Sie als Requisiten übergeben oder in Ihrem Status festlegen, geändert werden.

  • Funktionskomponenten sind solche, die keine Lebenszyklusfunktionen haben. Sie sind angeblich staatenlos, aber sie sind so schön und sauber, dass wir jetzt Haken haben (seit React 16.8), damit Sie immer noch einen Zustand haben können. Ich denke, sie sind nur "saubere Komponenten".

JackyJohnson
quelle