Problem
Ich setze eine Reaktion ref
mit einer Inline-Funktionsdefinition
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
dann wird im componentDidMount
DOM keine Referenz gesetzt
componentDidMount = () => {
// this.drawerRef is not defined
Meines ref
Wissens nach sollte der Rückruf während des Mount ausgeführt werden. Das Hinzufügen von console.log
Anweisungen componentDidMount
wird jedoch vor der Ref-Rückruffunktion aufgerufen .
Andere Codebeispiele, die ich mir zum Beispiel in dieser Diskussion über Github angesehen habe, weisen auf dieselbe Annahme hin, componentDidMount
die nach allen in ref
definierten Rückrufen aufgerufen werden sollte. Dies wird render
sogar in der Konversation angegeben
Also wird componentDidMount ausgelöst, nachdem alle Ref-Rückrufe ausgeführt wurden?
Ja.
Ich benutze reagieren 15.4.1
Etwas anderes habe ich versucht
Um zu überprüfen, ob die ref
Funktion aufgerufen wurde, habe ich versucht, sie für die Klasse als solche zu definieren
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
dann in render
<div className="drawer" ref={this.setDrawerRef}>
Die Konsolenprotokollierung in diesem Fall zeigt, dass der Rückruf tatsächlich nachher aufgerufen wird componentDidMount
quelle
this
aus 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.render
und damit sein leveraging erforderlichcomponentDidUpdate
, dacomponentDidMount
nicht Teil der Aktualisierung ist Lebenszyklus . Wahrscheinlich nicht Ihr Problem, aber ich dachte, es könnte sich lohnen, es als mögliche Lösung anzusprechen.ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
aber dies scheint nicht zu stimmen :(ref = {ref => { this.drawerRef = ref }}
Referenzpfeils lautet: 2. Sogar Refs werden vor componentDidMount aufgerufen. Auf ref kann nur nach dem ersten Rendern zugegriffen werden, wenn das div in Ihrem Fall gerendert wird. Sie müssen also in der Lage sein, auf die Referenz in der nächsten Ebene zuzugreifen, dh in componentWillReceiveProps mitthis.drawerRef
3. Wenn Sie versuchen, vor dem ersten Mounten darauf zuzugreifen, erhalten Sie nur einen der beiden undefinierten Werte von ref.Antworten:
Kurze Antwort:
React garantiert, dass Refs vor
componentDidMount
odercomponentDidUpdate
Hooks gesetzt werden. Aber nur für Kinder, die tatsächlich gerendert wurden .componentDidMount() { // can use any refs here } componentDidUpdate() { // can use any refs here } render() { // as long as those refs were rendered! 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.isLoading
isttrue
, sollten Sie nicht erwarten ,this._setRef
bevor aufgerufen werdencomponentDidMount
.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 dierender()
Methode sagte, dass es nicht gerendert werden sollte.In diesem Beispiel wird also nur
componentDidMount
ausgelöst. Wennthis.state.loading
false
Sie jedoch Änderungen an vornehmen , wird diesthis._setRef
zuerst 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
MyPanel
nichtprops.children
in seiner Ausgabe enthalten:function MyPanel(props) { // ignore props.children 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 stattdessenReactDOM.render()
ein Lebenszyklus-Hook aufgerufen. Ein Beispiel dafür finden Sie hier . In diesem Beispiel rendern wir:<MyModal> <div ref={this.setRef} /> </MyModal>
Aber
MyModal
führt einenReactDOM.render()
Aufruf in seinemcomponentDidUpdate
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.render
Aufrufen 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
setState
zum Speichern von Refs verwendenStellen Sie sicher, dass Sie
setState
den Ref nicht im Ref-Rückruf speichern, da er asynchron ist und zuerst ausgeführt wird, bevor er "fertig" istcomponentDidMount
.Immer noch ein Problem?
Wenn keiner der oben genannten Tipps hilft, melden Sie ein Problem in React und wir werden einen Blick darauf werfen.
quelle
Eine andere Beobachtung des Problems.
Ich habe festgestellt, dass das Problem nur im Entwicklungsmodus aufgetreten ist. Nach weiteren Untersuchungen stellte ich fest, dass das Deaktivieren
react-hot-loader
in meiner Webpack-Konfiguration dieses Problem verhindert.ich benutze
Und es ist eine Elektronen-App.
Meine teilweise Webpack-Entwicklungskonfiguration
const webpack = require('webpack') const merge = require('webpack-merge') const baseConfig = require('./webpack.config.base') module.exports = merge(baseConfig, { entry: [ // REMOVED THIS -> 'react-hot-loader/patch', `webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr`, '@babel/polyfill', './app/index' ], ... })
Es wurde verdächtig, als ich sah, dass die Verwendung der Inline-Funktion in render () funktionierte, die Verwendung einer gebundenen Methode jedoch abstürzte.
Funktioniert auf jeden Fall
class MyComponent { render () { return ( <input ref={(el) => {this.inputField = el}}/> ) } }
Absturz mit React-Hot-Loader (ref ist in componentDidMount undefiniert)
class MyComponent { constructor (props) { super(props) this.inputRef = this.inputRef.bind(this) } inputRef (input) { this.inputField = input } render () { return ( <input ref={this.inputRef}/> ) } }
Um ehrlich zu sein, war Hot Reload oft problematisch, um "richtig" zu werden. Da Entwickler-Tools schnell aktualisiert werden, hat jedes Projekt eine andere Konfiguration. Vielleicht könnte meine spezielle Konfiguration behoben werden. Ich werde Sie hier informieren, wenn dies der Fall ist.
quelle
Das Problem kann auch auftreten, wenn Sie versuchen, eine Referenz einer nicht gemounteten Komponente zu verwenden, z. B. eine Referenz in setinterval, und das festgelegte Intervall während der Bereitstellung der Komponente nicht löschen.
componentDidMount(){ interval_holder = setInterval(() => { this.myref = "something";//accessing ref of a component }, 2000); }
immer klares Intervall wie zum Beispiel,
componentWillUnmount(){ clearInterval(interval_holder) }
quelle