componentDidMount, ref geri aramadan ÖNCE çağrıldı


87

Sorun

refSatır içi bir işlev tanımı kullanarak bir tepki ayarlıyorum

render = () => {
    return (
        <div className="drawer" ref={drawer => this.drawerRef = drawer}>

sonra componentDidMountDOM referansı ayarlanmadı

componentDidMount = () => {
    // this.drawerRef is not defined

Anladığım kadarıyla refgeri aramanın bağlama sırasında çalıştırılması gerekir, ancak console.logifadelerin eklenmesi ref geri arama işlevinden öncecomponentDidMount çağrılır .

Ben mesela baktım Diğer kod örnekleri bu tartışma github aynı varsayımını göstermektedir componentDidMountçağrılmalıdır sonra herhangi reftanımlanan geri aramaları renderbile oluyor, konuşma belirtilen

Yani componentDidMount, tüm ref geri aramaları yürütüldükten sonra tetikleniyor mu?

Evet.

React 15.4.1 kullanıyorum

Denediğim başka bir şey

refİşlevin çağrıldığını doğrulamak için sınıfta bu şekilde tanımlamayı denedim

setDrawerRef = (drawer) => {
  this.drawerRef = drawer;
}

daha sonra render

<div className="drawer" ref={this.setDrawerRef}>

Bu durumda konsol günlüğü, geri aramanın gerçekten de sonra çağrıldığını ortaya çıkarır. componentDidMount


6
Yanılıyor olabilirim, ancak işleme metodu için ok işlevini kullandığınızda this, sınıfınızın dışındaki sözcük kapsamından değerini yakalayacaktır . Sınıf yöntemleriniz için ok işlevi sözdiziminden kurtulmaya çalışın ve yardımcı olup olmadığına bakın.
Yoshi

3
@GProst Bu benim sorumun doğası. Console.log'u hem işleve koydum hem de componentDidMount ilk olarak çalışıyor, ref geri çağırma ikinci.
quickshiftin

3
Az önce benzer bir sorun yaşadık - bizim için, temelde, başlangıçta gözden kaçırdık renderve bu nedenle , güncelleme yaşam döngüsünün bir parçası olmadığı için componentDidUpdate, yararlanmaya ihtiyacımız vardı . Muhtemelen senin sorunun değil, ama potansiyel bir çözüm olarak ortaya atılmaya değeceğini düşündüm. componentDidMount
Alexander Nied

4
React 16 ile aynı. Dokümantasyon açıkça belirtiyor ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.ama bu doğru görünmüyor :(
Ryan H.

1
1. ref ok bildirimi: ref = {ref => { this.drawerRef = ref }}2. bile refs, componentDidMount'tan önce çağrılır; ref sadece ilk işlemeden sonra sizin durumunuzdaki div işlendiğinde erişilebilir. Bu nedenle, ref'e bir sonraki seviyede, yani componentWillReceiveProps'ta this.drawerRef3. kullanarak erişebilmelisiniz. İlk mount'dan önce erişmeye çalışırsanız, sadece tanımsız ref değerlerini alırsınız.
bh4r4

Yanıtlar:


156

Kısa cevap:

React, referansların önceden ayarlandığını componentDidMountveya componentDidUpdateçengelleri garantiler . Ama sadece gerçekten dönüştürülmüş çocuklar için .

componentDidMount() {
  // can use any refs here
}

componentDidUpdate() {
  // can use any refs here
}

render() {
  // as long as those refs were rendered!
  return <div ref={/* ... */} />;
}

Bu, "React her zaman bu kancalar çalışmadan önce tüm referansları ayarlar" anlamına gelmez .
Ref bazı örneklere atalım yok set olsun.


İşlenmemiş öğeler için referanslar ayarlanmıyor

React, yalnızca render'dan gerçekten döndürdüğünüz öğeler için ref geri aramalarını çağırır .

Bu, kodunuz şöyle görünüyorsa

render() {
  if (this.state.isLoading) {
    return <h1>Loading</h1>;
  }

  return <div ref={this._setRef} />;
}

ve başlangıçta this.state.isLoadingolduğu truesen gerektiğini değil beklemek this._setRefönce çağrılacak componentDidMount.

Bu mantıklı olmalıdır: Eğer ilk işleminiz geri döndüyse <h1>Loading</h1>, React'in başka bir koşulda ref eklenmesini gerektiren başka bir şey döndürdüğünü bilmesinin mümkün bir yolu yoktur. Ayrıca ref'i ayarlayacak hiçbir şey yoktur: yöntem <div>oluşturulmaması render()gerektiğini söylediği için öğe yaratılmadı .

Yani bu örnekle sadece componentDidMountateş edecek. Ancak, olarak this.state.loadingdeğiştirildiğindefalse , this._setRefönce eklendiğini göreceksiniz ve ardından componentDidUpdateateşleyeceksiniz.


Diğer bileşenlere dikkat edin

Not olduğunu ve diğer bileşenler aşağı hakemlerimizle çocukları geçirirseniz onlar önler render (ve soruna neden) o şey yapıyoruz bir şans var.

Örneğin, bu:

<MyPanel>
  <div ref={this.setRef} />
</MyPanel>

çıktısına MyPaneldahil edilmeseydi işe props.childrenyaramazdı:

function MyPanel(props) {
  // ignore props.children
  return <h1>Oops, no refs for you today!</h1>;
}

Yine, bu bir hata değil: DOM öğesi yaratılmadığı için React'in ref'i ayarlayabileceği hiçbir şey olmayacaktı .


Referanslar, yuvalanmış bir ortama geçirildiyse yaşam döngülerinden önce ayarlanamaz ReactDOM.render()

Önceki bölüme benzer şekilde, bir çocuğu başka bir bileşene ref ile iletirseniz, bu bileşenin ref'in zamanında eklenmesini engelleyen bir şey yapması mümkündür.

Örneğin, belki çocuğu geri getirmiyor render()ve bunun yerine ReactDOM.render()bir yaşam döngüsü kancasını arıyor . Bunun bir örneğini burada bulabilirsiniz . Bu örnekte şunları yapıyoruz:

<MyModal>
  <div ref={this.setRef} />
</MyModal>

Ama MyModalbir gerçekleştirir ReactDOM.render()aramayı onun componentDidUpdate yaşam döngüsü yöntemiyle:

componentDidUpdate() {
  ReactDOM.render(this.props.children, this.targetEl);
}

render() {
  return null;
}

React 16'dan beri, bir yaşam döngüsü boyunca bu tür üst düzey render çağrıları, tüm ağaç için yaşam döngüleri çalışana kadar ertelenecektir . Bu, zamanla eklenen referansları neden görmediğinizi açıklar.

Bu sorunun çözümü , iç içe çağrılar yerine portallar kullanmaktır ReactDOM.render:

render() {
  return ReactDOM.createPortal(this.props.children, this.targetEl);
}

Bu şekilde <div>ref ile yaptığımız şey aslında render çıktısına dahil edilir.

Bu nedenle, bu sorunla karşılaşırsanız, bileşeniniz ile ref arasında çocukları işlemeyi geciktirebilecek hiçbir şey olmadığını doğrulamanız gerekir.

setStateReferansları saklamak için kullanmayın

setStateRef içinde ref geri çağrısını saklamak için kullanmadığınızdan emin olun , çünkü eşzamansız olduğu ve "bitmeden" önce componentDidMountçalıştırılacaktır.


Hâlâ bir Sorun mu?

Yukarıdaki ipuçlarından hiçbiri yardımcı olmazsa, React'te bir sorun bildirin ve bir göz atalım.


2
Bu durumu açıklamak için cevabımda da bir düzenleme yaptım. İlk bölüme bakın. Bu yardımcı olur umarım!
Dan Abramov

Merhaba @DanAbramov bunun için teşekkürler! Ne yazık ki, onunla ilk karşılaştığımda tekrarlanabilir bir vaka geliştiremedim. Maalesef artık o proje üzerinde çalışmıyorum ve o zamandan beri reprodüksiyon yapamıyorum. Yine de soru yeterince popüler hale geldi, buna katılıyorum, tekrarlanabilir vakayı bulmaya çalışmanın anahtarı, birçok insan sorunu yaşıyor gibi görünüyor.
hızlı değiştirme

Sanırım çoğu durumda bunun bir yanlış anlaşılmadan kaynaklandığını düşünüyorum. React 15'te bu, yutulan bir hatadan dolayı da olabilir (React 16 daha iyi hata işleme sahiptir ve bunu önler). Bu olduğunda daha fazla vakayı incelemekten memnuniyet duyarım, bu nedenle bunları yorumlara eklemekten çekinmeyin.
Dan Abramov

Yardım eder! Bir önyükleyici olduğunu gerçekten fark etmedim.
Nazariy

1
Bu cevap bana gerçekten yardımcı oldu. Bazı boş "referanslar" referansı ile mücadele ediyordum ve "elementlerin" hiç işlenmediği ortaya çıktı.
MarkSkayff

1

Sorunun farklı bir gözlemi.

Sorunun yalnızca geliştirme modundayken meydana geldiğini fark ettim. Daha fazla araştırmadan sonra, react-hot-loaderWebpack yapılandırmamdaki devre dışı bırakmanın bu sorunu önlediğini öğrendim.

Ben kullanıyorum

  • "react-hot-loader": "3.1.3"
  • "web paketi": "4.10.2",

Ve bu bir elektron uygulaması.

Kısmi Web paketi geliştirme yapılandırmam

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'
  ],
  ...
})

Render () içinde satır içi işlevi kullanmanın çalıştığını, ancak bağlı bir yöntem kullanmanın kilitlendiğini görünce şüpheli oldu.

Her durumda çalışır

class MyComponent {
  render () {
    return (
      <input ref={(el) => {this.inputField = el}}/>
    )
  }
}

React-hot-loader ile çökme (ref, componentDidMount'ta tanımsızdır)

class MyComponent {
  constructor (props) {
    super(props)
    this.inputRef = this.inputRef.bind(this)
  }

  inputRef (input) {
    this.inputField = input
  }

  render () {
    return (
      <input ref={this.inputRef}/>
    )
  }
}

Dürüst olmak gerekirse, çalışırken yeniden doldurmanın "doğru" olması genellikle sorunludur. Geliştirme araçları hızla güncellenirken, her projenin farklı bir yapılandırması vardır. Belki benim özel yapılandırmam düzeltilebilir. Durum buysa size burada haber vereceğim.


Bu, CodePen'de neden bu konuda sorun yaşadığımı açıklayabilir, ancak bir satır içi işlevi kullanmak benim durumumda yardımcı olmadı.
robartsd

0

Bu sorun, setinterval içinde ref kullanmak gibi ve bileşen unmount sırasında ayar aralığını temizlemediğinizde, bağlanmamış bir bileşenin ref kullanmaya çalıştığınızda da ortaya çıkabilir.

componentDidMount(){
    interval_holder = setInterval(() => {
    this.myref = "something";//accessing ref of a component
    }, 2000);
  }

örneğin her zaman aralığı temizle,

componentWillUnmount(){
    clearInterval(interval_holder)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.