Krótka odpowiedź:
React gwarantuje, że refs są ustawione przed componentDidMountlub componentDidUpdatehooki. Ale tylko dla dzieci, które faktycznie zostały wyrenderowane .
componentDidMount() {
}
componentDidUpdate() {
}
render() {
return <div ref={/* ... */} />;
}
Zauważ, że nie oznacza to, że „Reakcja zawsze ustawia wszystkie odniesienia przed uruchomieniem tych haków”.
Spójrzmy na kilka przykładów, w których referencje nie są ustawiane.
Odniesienia nie są ustawiane dla elementów, które nie zostały wyrenderowane
React wywoła wywołania zwrotne tylko dla elementów, które faktycznie zwróciłeś z renderowania .
Oznacza to, że jeśli twój kod wygląda jak
render() {
if (this.state.isLoading) {
return <h1>Loading</h1>;
}
return <div ref={this._setRef} />;
}
i początkowo this.state.isLoadingjest true, należy nie oczekiwać this._setRef, aby być wywołana przed componentDidMount.
To powinno mieć sens: jeśli twój pierwszy render został zwrócony <h1>Loading</h1>, nie ma możliwości, aby React wiedział, że pod jakimś innym warunkiem zwraca coś innego, co wymaga dołączenia ref. Jest też nic, aby ustawić wg:<div> element nie został utworzony, ponieważ render()metoda powiedział, że nie powinno być renderowane.
Więc w tym przykładzie tylko componentDidMountodpali. Jednak po this.state.loadingzmianie nafalse , najpierw zobaczysz this._setRefzałączony, a następnie componentDidUpdatezostanie uruchomiony.
Uważaj na inne komponenty
Zauważ, że jeśli przekażesz dzieciom z referencjami do innych komponentów , jest szansa, że robią coś, co uniemożliwia renderowanie (i powoduje problem).
Na przykład to:
<MyPanel>
<div ref={this.setRef} />
</MyPanel>
nie działałby, gdyby MyPanelnie uwzględniał props.childrenw swoim wyjściu:
function MyPanel(props) {
return <h1>Oops, no refs for you today!</h1>;
}
Ponownie, to nie jest błąd: React nie miałby nic do ustawienia ref, ponieważ element DOM nie został utworzony .
Odniesienia nie są ustawiane przed cyklami życia, jeśli są przekazywane do zagnieżdżonego ReactDOM.render()
Podobnie jak w poprzedniej sekcji, jeśli przekazujesz element potomny z ref do innego składnika, możliwe jest, że ten składnik może zrobić coś, co uniemożliwia dołączenie ref w czasie.
Na przykład może nie zwraca dziecka z render(), a zamiast tego wywołuje ReactDOM.render()hak cyklu życia. Przykład tego można znaleźć tutaj . W tym przykładzie renderujemy:
<MyModal>
<div ref={this.setRef} />
</MyModal>
Ale MyModalwykonuje ReactDOM.render()wywołanie w swojej componentDidUpdate metodzie cyklu życia:
componentDidUpdate() {
ReactDOM.render(this.props.children, this.targetEl);
}
render() {
return null;
}
Od czasu React 16 takie wywołania renderowania najwyższego poziomu podczas cyklu życia będą opóźnione do czasu, gdy cykle życia zostaną uruchomione dla całego drzewa . To by wyjaśniało, dlaczego nie widzisz odnośników dołączonych na czas.
Rozwiązaniem tego problemu jest użycie
portali zamiast zagnieżdżonych ReactDOM.renderwywołań:
render() {
return ReactDOM.createPortal(this.props.children, this.targetEl);
}
W ten sposób nasz <div>z ref jest faktycznie uwzględniany w wyniku renderowania.
Jeśli więc napotkasz ten problem, musisz sprawdzić, czy między komponentem a referencją nie ma nic, co mogłoby opóźnić renderowanie elementów potomnych.
Nie używaj setStatedo przechowywania referencji
Upewnij się, że nie używasz setStatedo przechowywania ref w wywołaniu zwrotnym ref, ponieważ jest asynchroniczne i zanim zostanie „zakończone”, componentDidMountzostanie wykonane jako pierwsze.
Wciąż problem?
Jeśli żadna z powyższych wskazówek nie pomoże, zgłoś problem w Reakcie, a my się przyjrzymy.
thisz zakresu leksykalnego poza twoją klasą. Spróbuj pozbyć się składni funkcji strzałek dla metod klasy i zobacz, czy to pomaga.