Stateful vs. Stateless Funktionsteilen in React
() translation by (you can also view the original English article)
React ist eine beliebte JavaScript Front-End Bibliothek zum Erstellen interaktiver Benutzeroberflächen. React hat eine relativ flache Lernkurve, was einer der Gründe ist, warum es in letzter Zeit die ganze Aufmerksamkeit auf sich zieht.
Obwohl viele wichtige Konzepte behandelt werden müssen, sind Komponenten zweifellos das Herz und die Seele von React. Ein gutes Verständnis der Komponenten sollte Ihnen das Leben als React-Entwickler erleichtern.
Voraussetzungen
Dieses Tutorial passt für die Anfänger, die React gelernt haben und einen besseren Überblick über die Komponenten benötigen. Wir beginnen mit den Grundlagen der Komponenten und gehen dann zu anspruchsvolleren Konzepten wie Komponentenmustern und dem Zeitpunkt der Verwendung dieser Muster über. Es wurden verschiedene Komponentenklassifizierungen behandelt, z.B. Klassen- und Funktionskomponenten, Stateful- und Stateeless-Komponenten sowie Container- und Präsentationskomponenten.
Bevor ich anfange, möchte ich Ihnen das Code-Snippet vorstellen, das wir in diesem Tutorial verwenden werden. Es ist ein einfacher Zähler, der mit React erstellt wurde. Ich werde im gesamten Tutorial auf einige Teile dieses Beispiels zurückgreifen.
Also, fangen wir an.
Was sind Komponenten?
Komponenten sind autarke, unabhängige Mikroeinheiten, die einen Teil Ihrer Benutzeroberfläche beschreiben. Die Benutzeroberfläche einer Anwendung kann in kleinere Komponenten aufgeteilt werden, wobei jede Komponente ihren eigenen Code, ihre eigene Struktur und API hat.
Facebook verfügt beispielsweise über Tausende von Funktionen, die beim Anzeigen der Webanwendung miteinander verbunden sind. Hier ist eine interessante Tatsache: Facebook umfasst 30.000 Komponenten und die Zahl wächst. Die Komponentenarchitektur hilft es Ihnen, jedes Stück isoliert zu betrachten. Jede Komponente kann alles in ihrem Umfang aktualisieren, ohne sich Gedanken darüber zu machen, wie sich dies auf andere Komponenten auswirkt.
Wenn wir die Benutzeroberfläche von Facebook als Beispiel nehmen, wäre die Suchleiste ein guter Kandidat für eine Komponente. Der Newsfeed von Facebook würde eine andere Komponente (oder eine Komponente, die viele Unterkomponenten hostet) erstellen. Alle Methoden und AJAX-Aufrufe, die sich mit der Suchleiste befassen, befinden sich in dieser Komponente.
Die Komponenten sind auch wiederverwendbar. Wenn Sie dieselbe Komponente an mehreren Stellen benötigen, ist dies einfach. Mithilfe der JSX-Syntax können Sie Ihre Komponenten überall dort deklarieren, wo sie angezeigt werden sollen.
1 |
<div> |
2 |
Current count: {this.state.count} |
3 |
<hr /> |
4 |
{/* Component reusability in action. */ } |
5 |
<Button sign = "+" count={this.state.count} |
6 |
updateCount = {this.handleCount.bind(this) }/> |
7 |
<Button sign = "-" count={this.state.count} |
8 |
updateCount = {this.handleCount.bind(this) }/> |
9 |
</div> |
Props and State
Komponenten benötigen Daten, mit denen sie arbeiten können. Es gibt zwei verschiedene Möglichkeiten, Komponenten und Daten zu kombinieren: entweder als props oder als state. Props and State bestimmen, was eine Komponente rendert und wie sie sich verhält. Beginnen wir mit Props.
props
Wenn Komponenten einfache JavaScript-Funktionen wären, wären Props die Funktionseingabe. Nach dieser Analogie akzeptiert eine Komponente eine Eingabe (was wir als Props bezeichnen), verarbeitet sie und rendert dann JSX-Code.



Obwohl die Daten in Props für eine Komponente zugänglich sind, lautet die Philosophie von React, dass Props unveränderlich und von oben nach unten sein sollten. Dies bedeutet, dass eine übergeordnete Komponente alle gewünschten Daten als Props an ihre untergeordneten Komponenten weitergeben kann, die untergeordnete Komponente ihre Props jedoch nicht ändern kann. Wenn Sie also versuchen, die Props wie unten beschrieben zu bearbeiten, wird der TypeError "Kann nicht schreibgeschützt zugewiesen werden" angezeigt.
1 |
const Button = (props) => { |
2 |
// props are read only
|
3 |
props.count =21; |
4 |
.
|
5 |
.
|
6 |
}
|
State
State ist ein Objekt, das der Komponente gehört, in der es deklariert ist. Ihr Umfang ist auf die aktuelle Komponente beschränkt. Eine Komponente kann ihren State initialisierenund bei Bedarf aktualisieren. State von den übergeordneten Komponenten ist eine Requisite der untergeordneten Komponente. Wenn State aus dem aktuellen Bereich herausgereicht wird, wird er als Props bezeichnet.
Nachdem wir die Grundlagen der Komponenten kennen, werfen wir einen Blick auf die grundlegende Klassifizierung von Komponenten.
Klassenkomponenten vs. Funktionskomponenten
Es gibt zwei Arten von React-Komponenten: entweder eine Klassenkomponente oder eine Funktionskomponente. Der Unterschied zwischen den beiden ist aus ihren Namen ersichtlich.
Funktionskomponenten
Funktionskomponenten sind nur JavaScript-Funktionen. Sie nehmen eine optionale Eingabe auf, die wir, wie bereits erwähnt, als Props bezeichnen.
Einige Entwickler bevorzugen die Verwendung der neuen ES6-Pfeilfunktionen zum Definieren von Komponenten. Pfeilfunktionen sind kompakter und bieten eine übersichtliche Syntax zum Schreiben von Funktionsausdrücken. Mithilfe einer Pfeilfunktion können wir die Verwendung von zwei Schlüsselwörtern, funktion
und return
, sowie zwei geschweiften Klammern überspringen. Mit der neuen Syntax können Sie eine Komponente wie folgt in einer einzelnen Zeile definieren.
1 |
const Hello = ({ name }) => (<div>Hello, {name}!</div>); |
Klassenkomponenten
Klassenkomponenten bieten mehr Funktionen und mit mehr Funktionen kommt mehr Gepäck. Der Hauptgrund für die Auswahl von Klassenkomponenten gegenüber Funktionskomponenten besteht darin, dass sie einen state
haben können.



Es gibt zwei Möglichkeiten, eine Klassenkomponente zu erstellen. Die traditionelle Methode ist die Verwendung von React.createClass()
. ES6 hat einen Syntaxzucker eingeführt, mit dem Sie Klassen schreiben können, die React.Component
erweitern. Beide Methoden sollen jedoch dasselbe bewirken.
Klassenkomponenten können auch ohne State existieren. Hier ist ein Beispiel für eine Klassenkomponente, die Eingabestützen akzeptiert und JSX rendert.
1 |
class Hello extends React.Component { |
2 |
constructor(props) { |
3 |
super(props); |
4 |
}
|
5 |
|
6 |
render() { |
7 |
return( |
8 |
<div> |
9 |
Hello {props} |
10 |
</div> |
11 |
)
|
12 |
}
|
13 |
}
|
Wir definieren eine Konstruktormethode, die Props als Eingabe akzeptiert. Innerhalb des Konstruktors rufen wir super() auf, um alles weiterzugeben, was von der übergeordneten Klasse geerbt wird. Hier sind einige Details, die Sie übersehen haben.
Erstens ist der Konstruktor beim Definieren einer Komponente optional. Im obigen Fall hat die Komponente kein State und der Konstruktor scheint nichts Nützliches zu tun. this.props
, die in render()
verwendet werden, funktionieren unabhängig davon, ob der Konstruktor definiert ist oder nicht. Hier ist jedoch etwas aus den offiziellen Dokumenten:
Klassenkomponenten sollten den Basiskonstruktor immer mitprops
aufrufen.
Ich empfehle Ihnen die Verwendung des Konstruktors für alle Klassenkomponenten.
Zweitens müssen Sie, wenn Sie einen Konstruktor verwenden, super()
aufrufen. Dies ist nicht optional, und ansonsten wird der Syntaxfehler "Fehlender Aufruf von super() im Konstruktor" angezeigt.
Und mein letzter Punkt betrifft die Verwendung von super()
vs. super(props)
. super(props)
sollte verwendet werden, wenn Sie this.props
im Konstruktor aufrufen möchten. Andernfalls reicht es aus, nur super()
zu verwenden.
Stateful Components vs. Stateless Components
Dies ist eine weitere beliebte Methode zur Klassifizierung von Komponenten. Und die Kriterien für die Klassifizierung sind einfach: die Komponenten mit State und die Komponenten ohne State.
Stateful Komponenten
Stateful-Komponenten sind immer Klassenkomponenten. Wie bereits erwähnt, haben stateful Komponenten ein State, der im Konstruktor initialisiert wird.
1 |
// Here is an excerpt from the counter example
|
2 |
constructor(props) { |
3 |
super(props); |
4 |
this.state = { count: 0 }; |
5 |
}
|
Wir haben ein State-Objekt erstellt und es mit einer Anzahl von 0 initialisiert. Es wird eine alternative Syntax vorgeschlagen, um dies zu vereinfachen. Sie werden als Klassenfelder bezeichnet. Es ist noch nicht Teil der ECMAScript-Spezifikation, aber wenn Sie einen Babel-Transpiler verwenden, sollte diese Syntax sofort funktionieren.
1 |
class App extends Component { |
2 |
|
3 |
/*
|
4 |
// Not required anymore
|
5 |
constructor() {
|
6 |
super();
|
7 |
this.state = {
|
8 |
count: 1
|
9 |
}
|
10 |
}
|
11 |
*/
|
12 |
|
13 |
state = { count: 1 }; |
14 |
|
15 |
handleCount(value) { |
16 |
this.setState((prevState) => ({count: prevState.count+value})); |
17 |
}
|
18 |
|
19 |
render() { |
20 |
// omitted for brevity
|
21 |
}
|
22 |
|
23 |
}
|
Mit dieser neuen Syntax können Sie die Verwendung des Konstruktors ganz vermeiden.
Wir können jetzt innerhalb der Klassenmethoden einschließlich render()
auf State zugreifen. Wenn Sie sie in render()
verwenden möchten, um den Wert der aktuellen Anzahl anzuzeigen, müssen Sie ihn wie folgt in geschweifte Klammern setzen:
1 |
render() { |
2 |
return ( |
3 |
Current count: {this.state.count} |
4 |
)
|
5 |
}
|
Das Schlüsselwort this
bezieht sich hier auf die Instanz der aktuellen Komponente.
Das Initialisieren des States reicht nicht aus - wir müssen in der Lage sein, State zu aktualisieren, um eine interaktive Anwendung zu erstellen. Wenn Sie dachten, das würde funktionieren, nein, wird es nicht.
1 |
//Wrong way
|
2 |
|
3 |
handleCount(value) { |
4 |
this.state.count = this.state.count +value; |
5 |
}
|
Reaktionskomponenten sind mit einer Methode namens setState zum Aktualisieren des States ausgestattet. setState akzeptiert ein Objekt, das neues State der count
enthält.
1 |
// This works
|
2 |
|
3 |
handleCount(value) { |
4 |
this.setState({count: this.state.count+ value}); |
5 |
}
|
setState()
akzeptiert ein Objekt als Eingabe und wir erhöhen den vorherigen Wert von count um 1, was wie erwartet funktioniert. Es gibt jedoch einen Haken. Wenn es mehrere setState-Aufrufe gibt, die einen vorherigen Wert des States lesen und einen neuen Wert in ihn schreiben, kann dies zu einer Race-Bedingung führen. Dies bedeutet, dass die Endergebnisse nicht mit den erwarteten Werten übereinstimmen.
Hier ist ein Beispiel, das es Ihnen klar machen soll. Versuchen Sie dies im obigen Code- und Box-Snippet.
1 |
// What is the expected output? Try it in the code sandbox.
|
2 |
handleCount(value) { |
3 |
this.setState({count: this.state.count+100}); |
4 |
this.setState({count: this.state.count+value}); |
5 |
this.setState({count: this.state.count-100}); |
6 |
}
|
Wir möchten, dass der setState die Anzahl um 100 erhöht, sie dann um 1 aktualisiert und dann die zuvor hinzugefügten 100 entfernt. Wenn setState den State-Übergang in der tatsächlichen Reihenfolge ausführt, erhalten wir das erwartete Verhalten. SetState ist jedoch asynchron, und mehrere setState-Aufrufe können für eine bessere Benutzeroberfläche und Leistung gestapelt werden. Der obige Code ergibt also ein Verhalten, das sich von dem unterscheidet, was wir erwarten.
Anstatt ein Objekt direkt zu übergeben, können Sie daher eine Updater-Funktion mit der Signatur übergeben:
1 |
(prevState, props) => stateChange |
2 |
prevState verweist auf den vorherigen State und ist garantiert auf dem neuesten Stand. Props beziehen sich auf die Props der Komponente, und wir benötigen hier keine Props, um ein State zu aktualisieren, sodass wir dies ignorieren können. Daher können wir es zum Aktualisieren des Status verwenden und die Rennbedingung vermeiden.
1 |
// The right way
|
2 |
|
3 |
handleCount(value) { |
4 |
|
5 |
this.setState((prevState) => { |
6 |
count: prevState.count +1 |
7 |
});
|
8 |
}
|
Mit setState()
wird die Komponente erneut gerendert, und Sie haben eine funktionierende Stateful-Komponente.
Stateless Komponenten
Sie können entweder eine Funktion oder eine Klasse zum Erstellen Stateless-Komponenten verwenden. Sofern Sie in Ihren Komponenten keinen Lebenszyklus-Hook verwenden müssen, sollten Sie sich für Stateless Funktionskomponenten entscheiden. Es gibt viele Vorteile, wenn Sie sich hier für die Verwendung Stateless Funktionskomponenten entscheiden. Sie sind einfach zu schreiben, zu verstehen und zu testen, und Sie können this
Schlüsselwort vollständig vermeiden. Ab React v16 bietet die Verwendung Stateless-Funktionskomponenten gegenüber Klassenkomponenten jedoch keine Leistungsvorteile mehr.
Der Nachteil ist, dass Sie keine Lifecycle-Hooks haben können. Die Lebenszyklusmethode ShouldComponentUpdate()
wird häufig verwendet, um die Leistung zu optimieren und manuell zu steuern, was erneut gerendert wird. Sie können das noch nicht mit Funktionskomponenten verwenden. Refs werden ebenfalls nicht unterstützt.
Containerkomponenten vs. Präsentationskomponenten
Dies ist ein weiteres Muster, das beim Schreiben von Komponenten sehr nützlich ist. Der Vorteil dieses Ansatzes besteht darin, dass die Verhaltenslogik von der Präsentationslogik getrennt ist.
Präsentationskomponenten
Präsentationskomponenten sind mit der Ansicht oder dem Aussehen der Dinge verbunden. Diese Komponenten akzeptieren Props von ihrem Container-Gegenstück und rendern sie. Alles, was mit der Beschreibung der Benutzeroberfläche zu tun hat, sollte hier stehen.
Präsentationskomponenten sind wiederverwendbar und sollten von der Verhaltensschicht entkoppelt bleiben. Eine Präsentationskomponente empfängt die Daten und Rückrufe ausschließlich über Props. Wenn ein Ereignis auftritt, wie das Drücken einer Taste, führt sie über Props einen Rückruf an die Containerkomponente durch, um eine Ereignisbehandlungsmethode aufzurufen.
Funktionskomponenten sollten Ihre erste Wahl für das Schreiben von Präsentationskomponenten sein, es sei denn, ein State ist erforderlich. Wenn eine Präsentationskomponente einen State erfordert, sollte sie sich mit dem UI-State und nicht mit tatsächlichen Daten befassen. Die Präsentationskomponente interagiert nicht mit dem Redux-Speicher und führt keine API-Aufrufe durch.
Containerkomponenten
Containerkomponenten befassen sich mit dem Verhaltensteil. Eine Containerkomponente teilt der Präsentationskomponente mit, was mit Propsgerendert werden soll. Es sollte keine eingeschränkten DOM-Markups und -Stile enthalten. Wenn Sie Redux verwenden, enthält eine Containerkomponente den Code, der eine Aktion an ein Geschäft sendet. Alternativ ist dies der Ort, an dem Sie Ihre API-Aufrufe platzieren und das Ergebnis im State der Komponente speichern sollten.
Die übliche Struktur besteht darin, dass sich oben eine Containerkomponente befindet, die die Daten als Props an die untergeordneten Präsentationskomponenten weitergibt. Dies funktioniert für kleinere Projekte; Wenn das Projekt jedoch größer wird und Sie viele Zwischenkomponenten haben, die nur Props akzeptieren und diese an untergeordnete Komponenten weitergeben, wird dies unangenehm und schwer zu warten. In diesem Fall ist es besser, eine für die Blattkomponente eindeutige Containerkomponente zu erstellen. Dies entlastet die Zwischenkomponenten.
Was ist eine PureComponent?
In React-Kreisen wird der Begriff reine Komponente sehr häufig zu hören sein, und dann gibt es React.PureComponent
. Wenn Sie neu bei React sind, klingt dies möglicherweise etwas verwirrend. Eine Komponente gilt als rein, wenn garantiert wird, dass sie bei gleichen Props und Zuständen dasselbe Ergebnis liefert. Eine Funktionskomponente ist ein gutes Beispiel für eine reine Komponente, da Sie anhand einer Eingabe wissen, was gerendert wird.
1 |
const HelloWorld = ({name}) => ( |
2 |
<div>{`Hi ${name}`}</div> |
3 |
);
|
Klassenkomponenten können auch rein sein, solange ihre Props und ihr Zustand unveränderlich sind. Wenn Sie eine Komponente mit einem 'tiefen' unveränderlichen Satz von Props und State haben, verfügt die React-API über eine sogenannte PureComponent
. React.PureComponent
ähnelt React.Component
, implementiert jedoch die ShouldComponentUpdate()
-Methode etwas anders. ShouldComponentUpdate()
wird aufgerufen, bevor etwas erneut gerendert wird. Das Standardverhalten ist, dass true zurückgegeben wird, sodass bei jeder Änderung des States oder der Props die Komponente erneut gerendert wird.
1 |
shouldComponentUpdate(nextProps, nextState) { |
2 |
return true; |
3 |
}
|
Mit PureComponent wird jedoch ein flacher Vergleich von Objekten durchgeführt. Flacher Vergleich bedeutet, dass Sie den unmittelbaren Inhalt der Objekte vergleichen, anstatt alle Schlüssel/Wert-Paare des Objekts rekursiv zu vergleichen. Es werden also nur die Objektreferenzen verglichen, und wenn das State/die Props mutiert sind, funktioniert es nicht wie beabsichtigt.
React.PureComponent
wird zur Optimierung der Leistung verwendet, und es gibt keinen Grund, warum Sie die Verwendung in Betracht ziehen sollten, es sei denn, Sie stoßen auf ein Leistungsproblem.
Letzte Gedanken
Stateless Funktionskomponenten sind eleganter und normalerweise eine gute Wahl für den Aufbau der Präsentationskomponenten. Da es sich nur um Funktionen handelt, fällt es Ihnen nicht schwer, sie zu schreiben und zu verstehen, und außerdem sind sie kinderleicht zu testen.
Es ist zu beachten, dass Stateless-Funktionskomponenten in Bezug auf Optimierung und Leistung nicht die Oberhand haben, da sie keinen ShouldComponentUpdate()
- Hook haben. Dies kann sich in zukünftigen Versionen von React ändern, in denen Funktionskomponenten möglicherweise für eine bessere Leistung optimiert werden. Wenn Sie die Leistung jedoch nicht kritisieren, sollten Sie sich an Funktionskomponenten für die Ansicht/Präsentation und an Stateful-Class-Komponenten für den Container halten.
Hoffentlich hat Ihnen dieses Tutorial einen allgemeinen Überblick über die komponentenbasierte Architektur und die verschiedenen Komponentenmuster in React gegeben. Was denkst du darüber? Teile sie durch die Kommentare.
In den letzten Jahren hat React an Popularität gewonnen. Tatsächlich gibt es in Envato Market eine Reihe von Artikeln, die zum Kauf, zur Überprüfung, Implementierung usw. verfügbar sind. Wenn Sie nach zusätzlichen Ressourcen rund um React suchen, zögern Sie nicht, diese zu überprüfen.