Komponenten sind die Bausteine von React-Anwendungen. Es ist fast unmöglich, eine React-Anwendung zu erstellen und keine Komponenten zu verwenden. Es ist so verbreitet, dass einige Drittanbieterpakete Ihnen Komponenten zur Verfügung stellen, die Sie zur Integration von Funktionalitäten in Ihre Anwendung verwenden können.
Diese Komponenten von Drittanbietern sind in der Regel wiederverwendbar. Der Unterschied zwischen ihnen und Komponenten, die Sie wahrscheinlich in Ihrer Anwendung haben, liegt in der Spezifität.
Hier ist, was ich meine. Nehmen wir an, Sie führen ein Unternehmen, das Poloshirts verkauft. Sie können zwei Dinge tun:
- Sie können Polos herstellen, die bereits ein Design haben, oder
- Sie können den Käufer das gewünschte Design auswählen lassen.
Einige grundlegende Dinge werden konstant sein, wie z. B. dass alle Polos kurzärmelig sein sollten. Aber der Benutzer kann Variationen der Hemden auswählen, wie Farbe und Größe. Ein kurzärmeliges Polo wäre in diesem Fall eine gute React-Komponente: Es ist dasselbe Produkt mit verschiedenen Variationen.
Nehmen wir nun an, Sie arbeiten an einem Anmeldeformular. Wie Poloshirts hat das Formular konsistente Merkmale, aber anstelle von Größen- und Farbvariationen würden wir Eingabefelder, eine Absendetaste und vielleicht sogar einen Link zum Zurücksetzen des Passworts betrachten. Dies kann komponentisiert und mit Variationen bei den Eingaben, Schaltflächen, Links usw. implementiert werden.
Beispiel für ein Eingabeelement
Betrachten wir dies aus der Perspektive der Erstellung eines Eingabefeldes für Ihr Formular. Hier ist, wie eine typische Texteingabe als React-Komponente aussehen würde:
class Form extends React.Component {
this.state = {
username: ''
}
handleChange = (event) => {
this.setSate({ username: event.target.value })
}
render() {
return (
<input
name="username"
type={type}
placeholder="Enter username"
onChange={this.handleChange}
value={this.state.username}
/>
)
}
}
Um dieses Eingabeelement an anderen Stellen und in anderen Projekten wiederverwenden zu können, müssen wir es in eine eigene Komponente extrahieren. Nennen wir sie FormInput.
const FormInput = ({
name,
type,
placeholder,
onChange,
className,
value,
error,
children,
label,
...props
}) => {
return (
<React.Fragment>
<label htmlFor={name}>{label}</label>
<input
id={name}
name={name}
type={type}
placeholder={placeholder}
onChange={onChange}
value={value}
className={className}
style={error && {border: 'solid 1px red'}}
/>
{ error && <p>{ error }</p>}
</React.Fragment>
)
}
FormInput.defaultProps = {
type: "text",
className: ""
}
FormInput.propTypes = {
name: PropTypes.string.isRequired,
type: PropTypes.string,
placeholder: PropTypes.string.isRequired,
type: PropTypes.oneOf(['text', 'number', 'password']),
className: PropTypes.string,
value: PropTypes.any,
onChange: PropTypes.func.isRequired
}
Die Komponente akzeptiert bestimmte Props, wie z. B. die Attribute, die wir benötigen, um die Eingabe mit gültigem Markup zu erstellen, einschließlich des Platzhalters, des Werts und des Namens. Wir richten das Eingabeelement in der Renderfunktion ein und setzen die Attributwerte als die an die Komponente übergebenen Props. Wir binden die Eingabe sogar an ein Label, um sicherzustellen, dass sie immer zusammengehören. Sie können sehen, dass wir keine Annahmen treffen, indem wir etwas vordefinieren. Die Idee ist, sicherzustellen, dass die Komponente in möglichst vielen Szenarien verwendet werden kann.
Dies ist eine gute Komponente, da sie gutes Markup erzwingt (etwas, das Brad Frost als dumb React bezeichnet) und zeigt, dass nicht jede Komponente eine hochkomplexe Funktionalität sein muss. Andererseits, wenn wir über etwas sehr Einfaches sprechen würden, sagen wir eine statische Überschrift, dann könnte die Verwendung einer React-Komponente übertrieben sein. Der mögliche Maßstab für die Erstellung einer wiederverwendbaren Komponente wäre wahrscheinlich, wenn Sie dieselbe Funktionalität an anderen Stellen einer Anwendung benötigen. Es gibt *im Allgemeinen* keinen Bedarf für eine "wiederverwendbare" Komponente, wenn diese Komponente nur einmal verwendet wird.
Wir können unsere Eingabekomponente in einer anderen Komponente, der LoginPage, verwenden.
class LoginPage extends React.Component {
state = {
user: {
username: "",
password: ""
},
errors: {},
submitted: false
};
handleChange = event => {
const { user } = this.state;
user[event.target.name] = event.target.value;
this.setState({ user });
};
onSubmit = () => {
const {
user: { username, password }
} = this.state;
let err = {};
if (!username) {
err.username = "Enter your username!";
}
if (password.length < 8) {
err.password = "Password must be at least 8 characters!";
}
this.setState({ errors: err }, () => {
if (Object.getOwnPropertyNames(this.state.errors).length === 0) {
this.setState({ submitted: true });
}
});
};
render() {
const {
submitted,
errors,
user: { username, password }
} = this.state;
return (
<React.Fragment>
{submitted ? (
<p>Welcome onboard, {username}!</p>
) : (
<React.Fragment>
<h3>Login!</h3>
<FormInput
label="Username"
name="username"
type="text"
value={username}
onChange={this.handleChange}
placeholder="Enter username..."
error={errors.username}
required
className="input"
/>
<FormInput
label="Password"
name="password"
type="password"
value={password}
onChange={this.handleChange}
placeholder="Enter password..."
error={errors.password}
className="input"
required
/>
<Button
type="submit"
label="Submit"
className="button"
handleClick={this.onSubmit}
/>
</React.Fragment>
)}
</React.Fragment>
);
}
}
Sehen Sie, wie LoginPage die FormInput zweimal verwendet? Wir verwenden sie sowohl als Texteingabe für einen Benutzernamen als auch als weitere Texteingabe für ein Passwort. Wenn wir Änderungen daran vornehmen möchten, wie die Eingabe funktioniert, können wir diese Änderungen in der FormInput-Komponentendatei vornehmen, die wir erstellt haben, und sie werden auf jede Instanz angewendet, in der die Eingabekomponente verwendet wird. Das ist der grundlegende Vorteil von wiederverwendbaren Komponenten: Sie müssen sich nicht wiederholen.
Selbst die Fehler werden von der FormInput-Komponente angezeigt.
Die Funktion onSubmit validiert zuerst das user-Objekt, das wir aus dem Formular erhalten, und stellt sicher, dass es der Struktur entspricht, d. h. dass ein Wert für username vorhanden ist. Beachten Sie, dass wir die Funktionalität der Eingabe sogar erweitern können, wie wir es getan haben, um zu überprüfen, ob das Passwort mindestens acht Zeichen enthält.
Wenn Sie sich den Code ansehen, werden Sie sehen, dass dort eine Button-Komponente vorhanden ist. Das ist nicht dasselbe wie ein HTML-<button>-Element, sondern eine andere Komponente, die die Props entgegennimmt, die den Typ der gewünschten Schaltfläche (submit, reset, button), ihre Klasse, was beim Klicken zu tun ist, und die Beschriftung definieren. Es gibt viele andere Schaltflächenattribute, die wir integrieren könnten, um jeden benötigten Standard durchzusetzen.
const Button = (props) => (
<button
type={props.type}
className={props.className}
onClick={props.handleClick}
>
{props.label}
</button>
)
Hier ist unser finales Anmeldeformular, wenn all unsere Komponenten zusammengefügt sind.
Siehe den Pen
Wiederverwendbare Button-Komponente von Kingsley Silas Chijioke (@kinsomicrote)
auf CodePen.
Möchten Sie das selbst ausprobieren? Versuchen Sie, ein wiederverwendbares <select>-Element zu erstellen. Wenn das zu schwierig ist, können Sie mit einem <textarea>-Element beginnen, dann vielleicht mit einer Checkbox, bevor Sie sich mit <select> befassen. Die Kernidee ist, es allgemein zu gestalten. Ich würde gerne sehen, was Sie sich ausgedacht haben, also verlinken Sie Ihre Arbeit im Kommentarbereich!
Schön!
Nehmen wir an, Sie möchten für Passworteingaben ein "Auge"-Symbol mit Ein-/Ausblendfunktion hinzufügen.
Würden Sie diese Logik in die FormInput-Komponente einbauen und sie dann über die Prop "addPasswordReveal" aktivieren, oder lieber eine neue separate Komponente "FormInputPassword" erstellen?
Was ist, wenn Sie viele verschiedene Fälle für verschiedene Typen haben?
Danke, Roy.
Für das erste Szenario, das Sie erwähnten, werde ich so vorgehen, wie Sie es vorgeschlagen haben – das Symbol hinzufügen und dann einen booleschen Wert verwenden, um es ein- oder auszublenden. Wenn ich verschiedene Typen haben möchte, kann ich entweder eine neue Komponente namens FormInputPassword erstellen, wie Sie erwähnt haben. Aber ich befürchte, dass dies nicht notwendig sein könnte. Also würde ich mich dafür entscheiden, eine Namens-Prop zu haben, um den Typ des anzuzeigenden Symbols zu bestimmen. Standardmäßig kann ich den Wert als false festlegen, so dass, wenn ein Name angegeben wird, das passende Symbol verwendet wird.
Ich hoffe, das beantwortet Ihre Frage.
Hallo, kurze Anmerkung zur Barrierefreiheit. Sie haben das Label und die Eingabe mit dem falschen Attribut verbunden. Das for-Attribut auf dem Label verweist auf eine ID. Leider haben Sie die ID nicht auf dem Eingabefeld gesetzt. Daher können Sie das Label nicht anklicken, um die Eingabe zu fokussieren.
Wenn Sie kein for und keine ID verwenden möchten, könnten Sie das Eingabeelement in das Label einwickeln.
Hallo Jonas,
Danke, dass Sie mich darauf aufmerksam gemacht haben, ich habe den Beitrag und die Demo aktualisiert.
Ich weiß das zu schätzen.
Toller Artikel, danke. Ich werde üben.
Hallo Silas,
Danke für den Artikel, er ist sehr gut geschrieben. Allerdings denke ich nicht, dass das ein gutes Beispiel ist. Wenn Sie das
<input>-Element kapseln, schaffen Sie im Wesentlichen eine neue API für den Entwickler, sodass der Entwickler die API nachschlagen muss, bevor er ein neues<input>-Element erstellt. Um dies sinnvoll zu kapseln, müssen Sie mehr zur Komponente hinzufügen, sonst ist sie nicht DRY. Zum Beispiel Styling-Klassen, Ausblenden des Eingabezustands, Barrierefreiheit, Eingabevalidierung. Ich habe dieses Problem bei einer Bootstrap-REACT-Bibliothek gesehen – alle Optionen, die normalerweise mit zusätzlichen Klassen aktiviert würden, wurden in Attribute/Eigenschaften umgewandelt, manchmal mit subtil unterschiedlichen Namen.John