Problem
Ustawiam odpowiedź, ref
używając definicji funkcji wbudowanej
render = () => {
return (
<div className="drawer" ref={drawer => this.drawerRef = drawer}>
wtedy w componentDidMount
DOM odwołanie nie jest ustawione
componentDidMount = () => {
// this.drawerRef is not defined
Rozumiem, że ref
wywołanie zwrotne powinno być uruchamiane podczas montowania, jednak dodawanie console.log
instrukcji reveals componentDidMount
jest wywoływane przed funkcją ref callback.
Inne przykłady kodu, które przeglądałem, na przykład ta dyskusja na githubie wskazują na to samo założenie, componentDidMount
należy wywołać po każdym ref
wywołaniu zwrotnym zdefiniowanym w render
, jest to nawet podane w rozmowie
Czyli componentDidMount jest uruchamiane po wykonaniu wszystkich wywołań zwrotnych?
Tak.
Korzystam z React 15.4.1
Próbowałem czegoś innego
Aby sprawdzić, czy ref
funkcja została wywołana, próbowałem zdefiniować ją w klasie jako takiej
setDrawerRef = (drawer) => {
this.drawerRef = drawer;
}
potem w render
<div className="drawer" ref={this.setDrawerRef}>
Rejestrowanie konsoli w tym przypadku ujawnia, że wywołanie zwrotne jest rzeczywiście wywoływane po componentDidMount
źródło
this
z zakresu leksykalnego poza twoją klasą. Spróbuj pozbyć się składni funkcji strzałek dla metod klasy i zobacz, czy to pomaga.render
i dlatego musieliśmy go wykorzystaćcomponentDidUpdate
, ponieważcomponentDidMount
nie jest to część cyklu aktualizacji . Prawdopodobnie nie jest to twój problem, ale pomyślałem, że warto go poruszyć jako potencjalne rozwiązanie.ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.
ale to nie wydaje się być prawdą :(ref = {ref => { this.drawerRef = ref }}
2. parzyste referencje są wywoływane przed komponentem componentDidMount; ref można uzyskać tylko po początkowym renderowaniu, gdy renderowany jest element div w Twoim przypadku. Musisz więc mieć dostęp do ref na następnym poziomie, tj. W componentWillReceiveProps przy użyciuthis.drawerRef
3. Jeśli spróbujesz uzyskać dostęp przed pierwszym zamontowaniem, otrzymasz tylko niezdefiniowane wartości ref.Odpowiedzi:
Krótka odpowiedź:
React gwarantuje, że refs są ustawione przed
componentDidMount
lubcomponentDidUpdate
hooki. Ale tylko dla dzieci, które faktycznie zostały wyrenderowane .componentDidMount() { // can use any refs here } componentDidUpdate() { // can use any refs here } render() { // as long as those refs were rendered! 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.isLoading
jesttrue
, należy nie oczekiwaćthis._setRef
, aby być wywołana przedcomponentDidMount
.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
componentDidMount
odpali. Jednak pothis.state.loading
zmianie nafalse
, najpierw zobaczyszthis._setRef
załączony, a następniecomponentDidUpdate
zostanie 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
MyPanel
nie uwzględniałprops.children
w swoim wyjściu:function MyPanel(props) { // ignore props.children 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łujeReactDOM.render()
hak cyklu życia. Przykład tego można znaleźć tutaj . W tym przykładzie renderujemy:<MyModal> <div ref={this.setRef} /> </MyModal>
Ale
MyModal
wykonujeReactDOM.render()
wywołanie w swojejcomponentDidUpdate
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.render
wywoł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
setState
do przechowywania referencjiUpewnij się, że nie używasz
setState
do przechowywania ref w wywołaniu zwrotnym ref, ponieważ jest asynchroniczne i zanim zostanie „zakończone”,componentDidMount
zostanie 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.
źródło
Inna obserwacja problemu.
Zdałem sobie sprawę, że problem wystąpił tylko w trybie programistycznym. Po dokładniejszym zbadaniu stwierdziłem, że wyłączenie
react-hot-loader
w konfiguracji WebPacka zapobiega temu problemowi.ja używam
Jest to aplikacja elektronowa.
Moja częściowa konfiguracja deweloperska pakietu Webpack
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' ], ... })
Stało się podejrzane, gdy zobaczyłem, że użycie funkcji inline w render () działa, ale użycie metody związanej kończyło się awarią.
Działa w każdym przypadku
class MyComponent { render () { return ( <input ref={(el) => {this.inputField = el}}/> ) } }
Awaria z reaktywnym programem ładującym (ref nie jest zdefiniowany w componentDidMount)
class MyComponent { constructor (props) { super(props) this.inputRef = this.inputRef.bind(this) } inputRef (input) { this.inputField = input } render () { return ( <input ref={this.inputRef}/> ) } }
Szczerze mówiąc, ponowne ładowanie na gorąco często było problematyczne, aby uzyskać „właściwy”. Dzięki szybkiej aktualizacji narzędzi programistycznych każdy projekt ma inną konfigurację. Może moja konkretna konfiguracja mogłaby zostać naprawiona. Dam ci znać, jeśli tak jest.
źródło
Problem może się również pojawić, gdy spróbujesz użyć odwołania niezmontowanego komponentu, takiego jak użycie ref w setinterval i nie wyczyścisz ustawionego interwału podczas odmontowywania komponentu.
componentDidMount(){ interval_holder = setInterval(() => { this.myref = "something";//accessing ref of a component }, 2000); }
zawsze wyraźny odstęp, jak na przykład
componentWillUnmount(){ clearInterval(interval_holder) }
źródło