Kurze Antwort:
React garantiert, dass Refs vor componentDidMountoder componentDidUpdateHooks gesetzt werden. Aber nur für Kinder, die tatsächlich gerendert wurden .
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return <div ref={/* ... */} />;
}
Beachten Sie, dass dies nicht bedeutet, dass "React immer alle Refs setzt, bevor diese Hooks ausgeführt werden".
Schauen wir uns einige Beispiele an, bei denen die Refs nicht gesetzt werden.
Refs werden nicht für Elemente gesetzt, die nicht gerendert wurden
React ruft nur ref-Rückrufe für Elemente auf, die Sie tatsächlich vom Rendern zurückgegeben haben .
Dies bedeutet, wenn Ihr Code aussieht
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
und anfangs this.state.isLoadingist true, sollten Sie nicht erwarten , this._setRefbevor aufgerufen werden componentDidMount.
Dies sollte sinnvoll sein: Wenn Ihr erstes Rendering zurückgegeben wird <h1>Loading</h1>, kann React nicht erkennen, dass es unter anderen Bedingungen etwas anderes zurückgibt, für das ein Ref angehängt werden muss. Es gibt auch nichts, auf das der Verweis gesetzt werden kann: Das <div>Element wurde nicht erstellt, da die render()Methode sagte, dass es nicht gerendert werden sollte.
In diesem Beispiel wird also nur componentDidMountausgelöst. Wennthis.state.loadingfalse Sie jedoch Änderungen an vornehmen , wird dies this._setRefzuerst angehängt und dann ausgelöstcomponentDidUpdate .
Achten Sie auf andere Komponenten
Beachten Sie, dass Kinder, die Refs an andere Komponenten weitergeben, möglicherweise etwas tun, das das Rendern verhindert (und das Problem verursacht).
Zum Beispiel:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
würde nicht funktionieren, wenn MyPanelnicht props.childrenin seiner Ausgabe enthalten:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Auch hier ist es kein Fehler: Für React gibt es nichts, auf das der Verweis gesetzt werden könnte, da das DOM-Element nicht erstellt wurde .
Refs werden nicht vor Lebenszyklen festgelegt, wenn sie an einen verschachtelten übergeben werden ReactDOM.render()
Ähnlich wie im vorherigen Abschnitt ist es möglich, dass diese Komponente, wenn Sie ein untergeordnetes Element mit einem Verweis an eine andere Komponente übergeben, etwas unternimmt, das das rechtzeitige Anhängen des Verweises verhindert.
Zum Beispiel wird das Kind möglicherweise nicht zurückgegeben render(), sondern es wird stattdessen ReactDOM.render()ein Lebenszyklus-Hook aufgerufen. Ein Beispiel dafür finden Sie hier . In diesem Beispiel rendern wir:
<MyModal>
<div ref={this.setRef} />
</MyModal>
Aber MyModalführt einen ReactDOM.render()Aufruf in seinem componentDidUpdate Lebenszyklus - Methode:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
Seit React 16 werden solche Renderaufrufe der obersten Ebene während eines Lebenszyklus verzögert, bis Lebenszyklen für den gesamten Baum ausgeführt wurden . Dies würde erklären, warum Sie die Refs nicht rechtzeitig sehen.
Die Lösung für dieses Problem besteht darin,
Portale anstelle von verschachtelten ReactDOM.renderAufrufen zu verwenden:
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
Auf diese Weise ist unser <div>mit einem Verweis tatsächlich in der Renderausgabe enthalten.
Wenn Sie also auf dieses Problem stoßen, müssen Sie überprüfen, ob zwischen Ihrer Komponente und dem Verweis nichts vorhanden ist, was das Rendern von untergeordneten Elementen verzögern könnte.
Nicht setStatezum Speichern von Refs verwenden
Stellen Sie sicher, dass Sie setStateden Ref nicht im Ref-Rückruf speichern, da er asynchron ist und zuerst ausgeführt wird, bevor er "fertig" ist componentDidMount.
Immer noch ein Problem?
Wenn keiner der oben genannten Tipps hilft, melden Sie ein Problem in React und wir werden einen Blick darauf werfen.
thisaus dem lexikalischen Bereich außerhalb Ihrer Klasse erfasst . Versuchen Sie, die Pfeilfunktionssyntax für Ihre Klassenmethoden zu entfernen, und prüfen Sie, ob dies hilfreich ist.