Risposta breve:
React garantisce che refs siano impostati prima componentDidMount
o componentDidUpdate
hooks. Ma solo per i bambini che sono stati effettivamente resi .
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return <div ref={/* ... */} />;
}
Nota questo non significa che "React imposta sempre tutti i ref prima che questi hook vengano eseguiti".
Diamo un'occhiata ad alcuni esempi in cui gli arbitri non vengono impostati.
I riferimenti non vengono impostati per gli elementi che non sono stati renderizzati
React chiamerà solo i callback ref per gli elementi effettivamente restituiti dal rendering .
Ciò significa che se il tuo codice ha l'aspetto
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
e inizialmente this.state.isLoading
è true
, si dovrebbe non aspettare this._setRef
di essere chiamato prima componentDidMount
.
Questo dovrebbe avere senso: se il tuo primo rendering è tornato <h1>Loading</h1>
, non c'è modo per React di sapere che in qualche altra condizione restituisce qualcos'altro che necessita di un ref da allegare. Non c'è inoltre nulla su cui impostare il riferimento: l' <div>
elemento non è stato creato perché il filerender()
metodo diceva che non doveva essere renderizzato.
Quindi, con questo esempio, sparerà solo componentDidMount
. Tuttavia, quando this.state.loading
cambia infalse
, vedrai this._setRef
prima gli allegati e poi si componentDidUpdate
attiverà.
Fai attenzione agli altri componenti
Nota che se passi figli con ref ad altri componenti, c'è la possibilità che stiano facendo qualcosa che impedisce il rendering (e causa il problema).
Ad esempio, questo:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
non funzionerebbe se MyPanel
non includesse props.children
nel suo output:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Anche in questo caso, non è un bug: non ci sarebbe nulla per React su cui impostare ref perché l'elemento DOM non è stato creato .
I riferimenti non vengono impostati prima del ciclo di vita se vengono passati a un file nidificato ReactDOM.render()
Analogamente alla sezione precedente, se passi un bambino con un riferimento a un altro componente, è possibile che questo componente faccia qualcosa che impedisce di attaccare il riferimento in tempo.
Ad esempio, forse non sta restituendo il bambino da render()
e invece sta chiamando ReactDOM.render()
un hook del ciclo di vita. Puoi trovare un esempio di questo qui . In questo esempio, rendiamo:
<MyModal>
<div ref={this.setRef} />
</MyModal>
Ma MyModal
esegue una ReactDOM.render()
chiamata nel suo componentDidUpdate
metodo del ciclo di vita:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
A partire da React 16, tali chiamate di rendering di primo livello durante un ciclo di vita verranno ritardate finché i cicli di vita non saranno stati eseguiti per l'intero albero . Questo spiegherebbe perché non vedi gli arbitri allegati in tempo.
La soluzione a questo problema è utilizzare i
portali invece delle ReactDOM.render
chiamate annidate :
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
In questo modo il nostro <div>
con un riferimento è effettivamente incluso nell'output di rendering.
Quindi, se si verifica questo problema, è necessario verificare che non ci sia nulla tra il componente e il ref che potrebbe ritardare il rendering dei bambini.
Non utilizzare setState
per memorizzare ref
Assicurati di non utilizzare setState
per memorizzare il ref in ref callback, poiché è asincrono e prima che sia "finito", componentDidMount
verrà eseguito per primo.
Ancora un problema?
Se nessuno dei suggerimenti precedenti aiuta, segnala un problema in React e daremo un'occhiata.
this
dall'ambito lessicale al di fuori della tua classe. Prova a sbarazzarti della sintassi della funzione freccia per i metodi della tua classe e vedi se aiuta.