Weiterleiten von Refs
Weiterleiten von Refs ist eine Technik für die automatische Übergabe einer Ref durch eine Komponente an eines seiner Kinder. Normalerweise besteht für die meisten Komponenten innerhalb einer Anwendung kein Bedarf dafür. Nichtsdestotrotz, kann es für gewisse Arten von Komponenten nützlich sein, vor allem wenn es sich dabei um wiederverwendbare Komponenten-Bibliotheken handelt. Die gängigsten Szenarien werden unterhalb beschrieben.
Weiterleiten von Refs zu DOM-Komponenten
Stelle dir eine FancyButton
Komponente vor, welche das native button
DOM-Element rendert:
function FancyButton(props) {
return (
<button className="FancyButton">
{props.children}
</button>
);
}
React Komponenten verbergen ihre Implementierungsdetails, einschließlich der gerenderten Ausgabe. Andere Komponenten die FancyButton
benutzen, werden in den meisten Fällen keine Notwendigkeit für den Abruf einer Ref zum inneren button
DOM-Element haben. Das ist gut so, da dies eine zu starke Abhängigkeit unter den Komponenten auf die gegenseitige DOM-Struktur verhindert.
Trotz der Tatsache, dass solch eine Kapselung für Komponenten in der Anwendungsebene wie FeedStory
oder Comment
erwünscht ist, kann dies für “Blatt-Komponenten” mit einem hohen Wiederverwendbarkeitsgrad, wie FancyButton
oder MyTextInput
unpraktisch sein. Diese Komponenten werden oft in der ganzen Anwendung als reguläre DOM button
und input
auf ähnliche Weise eingesetzt und der Zugriff auf dessen DOM-Knoten könnte für die Regelung von Fokus, Auswahl oder Animationen unvermeidlich sein.
Die Weiterleitung von Refs ist ein Opt-In Feature, welches manchen Komponenten die Möglichkeit bereitstellt, eine erhaltene ref
, weiter nach unten zu einem Kind durchzulassen (in anderen Worten, “weiterzuleiten”).
Im unteren Beispiel benutzt FancyButton
React.forwardRef
, um die übermittelte ref
abzurufen und diese anschließend an den gerenderten DOM button
weiterzuleiten.
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children}
</button>
));
// Du kannst jetzt ein Ref direkt zum DOM button bekommen:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
In diesem Fall, können die Komponenten die FancyButton
nutzen, das Ref zum unterliegenden button
DOM-Knoten abrufen und dies bei bedarf nutzen—so als ob sie direkt auf den DOM button
zugreifen würden.
Hier ist eine Schritt für Schritt Erklärung was im oberen Beispiel passiert:
- Wir erstellen eine React Ref mit dem Aufruf von
React.createRef
und weisen es derref
Variable zu. - Wir geben unsere
ref
an<FancyButton ref={ref}>
weiter, in dem wir diese als JSX Attribut definieren. - React gibt die
ref
der(props, ref) => ...
Funktion innerhalb vonforwardRef
als zweites Argument weiter. - Mit der Definition als JSX Attribut, leiten wir das
ref
Argument weiter zum<button ref={ref}>
. - When das Ref zugewiesen ist, zeigt
ref.current
auf den<button>
DOM-Knoten.
Hinweis
Das zweite
ref
Argument existiert nur dann, wenn du eine Komponente mitReact.forwardRef
Aufruf definierst. Normale Funktion- oder Klassenkomponenten erhalten dasref
Argument nicht, es ist auch nicht in den Props verfügbar.Das Weiterleiten von Refs ist nicht nur auf DOM-Komponenten limitiert. Du kannst auch Refs zu Instanzen von Klassenkomponenten weiterleiten.
Hinweis für Betreiber von Komponentenbibliotheken
Wenn du anfängst forwardRef
in einer Komponentenbibliothek zu nutzen, solltest du es als funktionsgefährdende Änderung behandeln und eine neue Major-Version deiner Bibliothek veröffentlichen. Deine Bibliothek wird höchstwahrscheinlich ein wahrnehmbar anderes Verhalten aufweisen (z.B. Zuordnung von Refs, welche Typen werden exportiert), dies könnte negative Auswirkungen auf andere Anwendungen und Bibliotheken haben, die auf das alte Verhalten angewiesen sind.
Bedingte Anwendung von React.forwardRef
bei Vorhandensein, ist ebenso aus den gleichen Gründen nicht ratsam: es verändert das Verhalten deiner Bibliothek und könnte negative Auswirkungen auf die Anwendungen deiner User haben, wenn sie ein React-Upgrade durchführen.
Weiterleiten von Refs in Higher-Order-Komponenten
Diese Technik kann besonders in Verbindung mit Higher-Order-Komponenten (auch bekannt als HOC) nützlich sein. Beginnen wir mit einer Beispiel-HOC, welche die Eigenschaften einer Komponente in die Konsole loggt:
function logProps(WrappedComponent) { class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('Alte Eigenschaften:', prevProps);
console.log('Neue Eigenschaften:', this.props);
}
render() {
return <WrappedComponent {...this.props} />; }
}
return LogProps;
}
Die “logProps” HOC gibt alle props
an die Komponente durch, die von ihr umschlossen wird, aus diesem Grund wird die gerenderte Ausgabe gleich sein. Wir können zum Beispiel diese HOC nutzen, um alle Eigenschaften zu loggen, die an unsere “fancy button” Komponente weitergegeben werden:
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// Anstatt die FancyButton Komponente zu exportieren, exportieren wir LogProps.
// Es wird trotzdem der FancyButton gerendert.
export default logProps(FancyButton);
Folgendes muss im oberen Beispiel beachtet werden: es werden keine Refs weitergegeben. Das ist der Tatsache zu entnehmen, dass ref
keine Eigenschaft ist. Ähnlich dem key
, wird es anders von React behandelt. Wenn du eine Ref zu einer HOC hinzufügst, wird diese die äußerste Container-Komponente und nicht die umschlossene Komponente referenzieren.
Dies bedeutet, dass Refs die für unsere FancyButton
Komponente bestimmt waren, eigentlich der LogProps
Komponente zugewiesen sein werden:
import FancyButton from './FancyButton';
const ref = React.createRef();
// Die FancyButton Komponente die wir importieren ist die LogProps HOK.
// Auch wenn die gerenderte Ausgabe gleich bleibt,
// wird unsere Ref auf `LogProps` statt auf die innere FancyButton Komponente verweisen!
// Dies bedeutet, dass wir z.B. kein ref.current.focus() aufrufen können.
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}/>;
Glücklicherweise, können wir Refs explizit an die innere FancyButton
Komponente weiterleiten, in dem wir die React.forwardRef
API benutzen. React.forwardRef
akzeptiert eine render-Funktion, die props
und ref
Eigenschaften enthält und einen React-Knoten zurückgibt. Beispiel:
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// Weise die benutzerdefinierte Eigenschaft "forwardRef" als Ref zu
return <Component ref={forwardedRef} {...rest} />; }
}
// Beachte das zweite Attribut "ref", welches von React.forwardRef bereitgestellt wird.
// Wir können es an LogProps wie eine reguläre Eigenschaft weiterleiten, z.B. "forwardRef"
// Und es kann einer Komponente zugewiesen werden.
return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; });}
Anzeigen von benutzerdefinierten Namen in DevTools
React.forwardRef
akzeptiert eine render-Funktion. React DevTools nutzt diese Funktion um zu bestimmen, was für die Ref-weiterleitende Komponente angezeigt werden soll.
Zum Beispiel, folgende Komponente wird als ”ForwardRef” in DevTools aufscheinen.
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
Wenn du die render-Funktion benennst, wird dieser auch innerhalb von DevTools inkludiert (z.B. ”ForwardRef(myFunction)”):
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
Du kannst sogar die Eigenschaft displayName
setzen, um die umschlossene Komponente zu inkludieren.
function logProps(Component) {
class LogProps extends React.Component {
// ...
}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
// Weise dieser Komponente einen aussagekräftigen Namen in DevTools zu.
// z.B. "ForwardRef(logProps(MyComponent))"
const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}