ComponentsDidMount BEFORE रेफ कॉलबैक कहा जाता है


86

मुसीबत

मैं refएक इनलाइन फ़ंक्शन परिभाषा का उपयोग करके एक प्रतिक्रिया सेट कर रहा हूं

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

तब componentDidMountDOM संदर्भ में सेट नहीं किया गया है

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

मेरी समझ यह है कि refकॉलबैक को माउंट के दौरान चलाया जाना चाहिए, हालांकि console.logबयानों को जोड़ने से रिफ कॉलबैक फ़ंक्शन से पहलेcomponentDidMount कॉल किया जाता है।

उदाहरण के लिए मैंने जिन अन्य कोड नमूनों को देखा है, गितुब पर यह चर्चा उसी धारणा को इंगित करती है, componentDidMountजिसे किसी भी कॉलबैक में परिभाषित किए जाने के बाद बुलाया जाना चाहिए , यह बातचीत में भी कहा गया हैrefrender

सभी घटक कॉलबैक निष्पादित किए जाने के बाद कंपोनेंटडिमाउंट को निकाल दिया जाता है?

हाँ।

मैं 15.4.1 प्रतिक्रिया का उपयोग कर रहा हूं

कुछ और मैंने कोशिश की है

refफ़ंक्शन को सत्यापित करने के लिए बुलाया जा रहा था, मैंने इसे कक्षा में इस तरह परिभाषित करने की कोशिश की

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

फिर render

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

इस मामले में कंसोल प्रवेश कॉलबैक वास्तव में बुलाया जा रहा है का पता चलता है के बाद componentDidMount


6
मैं गलत हो सकता हूं, लेकिन जब आप रेंडर मेथोस के लिए एरो फंक्शन का उपयोग कर रहे होते हैं, तो यह thisआपकी कक्षा के बाहर लेक्सिकल स्कोप से वैल्यू कैप्चर करेगा । अपने क्लास के तरीकों के लिए एरो फ़ंक्शन सिंटैक्स से छुटकारा पाने की कोशिश करें और देखें कि क्या यह मदद करता है।
योशी

3
@Gstst यह मेरे सवाल का स्वभाव है। मैं दोनों कार्यों में कंसोल डाल देता हूं और कंपोनेंटडिमाउंट पहले चल रहा है, रिफ कॉलबैक दूसरा।
क्विकशिफ्टिन

3
बस इसी तरह का एक मुद्दा था- हमारे लिए, मूल रूप से, हमने इसे प्रारंभिक पर याद किया renderऔर इस प्रकार इसका लाभ उठाने की आवश्यकता थी componentDidUpdate, जैसा componentDidMountकि अद्यतन जीवनचक्र का हिस्सा नहीं है । शायद आपका मुद्दा नहीं है, लेकिन यह एक संभावित समाधान के रूप में उठाने लायक हो सकता है।
अलेक्जेंडर ने जुड

4
16 प्रतिक्रिया के साथ ही। प्रलेखन स्पष्ट रूप से बताता है, ref callbacks are invoked before componentDidMount or componentDidUpdate lifecycle hooks.लेकिन यह सच नहीं लगता है :(
रयान एच।

1
1. रेफरी ऐरो डिक्लेरेशन है: ref = {ref => { this.drawerRef = ref }}2. यहां तक ​​कि रिफंड को कंपोनेंटडीडमाउंट से पहले लगाया जाता है; रेफ को केवल शुरुआती रेंडर के बाद ही एक्सेस किया जा सकता है जब आपके मामले में डिव का प्रतिपादन किया गया हो। तो, आप घटक का उपयोग करते हुए अगले स्तर में रेफरी को एक्सेस करने में सक्षम this.drawerRefहोंगे।
bh4r4th

जवाबों:


153

संक्षिप्त जवाब:

प्रतिक्रिया की गारंटी देता है कि refs को पहले componentDidMountया componentDidUpdateहुक से सेट किया जाता है । लेकिन केवल उन बच्चों के लिए जो वास्तव में प्रस्तुत हुए

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

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

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

ध्यान दें कि इसका मतलब यह नहीं है कि "प्रतिक्रिया हमेशा इन हुकों को चलाने से पहले सभी रीफ़ सेट करें "।
आइए कुछ उदाहरण देखें जहां रेफरी सेट नहीं होते हैं।


Ref उन तत्वों के लिए सेट नहीं होते हैं, जिनका प्रतिपादन नहीं किया गया था

रिएक्ट केवल उन तत्वों के लिए रेफरी कॉलबैक कहेगा जिन्हें आपने वास्तव में रेंडर से लौटाया था

इसका मतलब है कि अगर आपका कोड दिखता है

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

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

और शुरू this.state.isLoadingमें true, आपको पहले बुलाए जाने की उम्मीद नहीं करनी चाहिए ।this._setRefcomponentDidMount

यह समझ में आना चाहिए: यदि आपका पहला रेंडर वापस आया है <h1>Loading</h1>, तो रिएक्ट के लिए यह जानने का कोई संभव तरीका नहीं है कि किसी अन्य शर्त के तहत यह कुछ और लौटाता है जिसे संलग्न होने के लिए रेफ की आवश्यकता होती है। वहाँ भी है : रेफरी को स्थापित करने के लिए कुछ भी नहीं<div> तत्व क्योंकि नहीं बनाया गया था render()विधि ने कहा कि यह रेंडर नहीं किया जाना चाहिए।

तो इस उदाहरण के साथ, केवल componentDidMountआग लग जाएगी। हालाँकि, जब this.state.loadingपरिवर्तन होता हैfalse , तो आप this._setRefपहले संलग्न देखेंगे , और फिर componentDidUpdateफायर करेंगे।


अन्य घटकों के लिए बाहर देखो

ध्यान दें कि यदि आप बच्चों को अन्य घटकों के लिए रेफरी के साथ पास करते हैं तो एक मौका है कि वे कुछ ऐसा कर रहे हैं जो प्रतिपादन को रोकता है (और समस्या का कारण बनता है)।

उदाहरण के लिए, यह:

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

काम नहीं करेगा अगर इसके आउटपुट में MyPanelशामिल नहीं है props.children:

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

फिर से, यह बग नहीं है: रीम को सेट करने के लिए रिएक्ट के लिए कुछ भी नहीं होगा क्योंकि DOM तत्व नहीं बनाया गया था


यदि वे एक नेस्टेड के लिए पारित कर रहे हैं, तो जीवन चक्र से पहले रेफ को सेट नहीं किया जाता है ReactDOM.render()

पिछले अनुभाग के समान, यदि आप एक बच्चे को रेफरी के साथ किसी अन्य घटक में पास करते हैं, तो संभव है कि यह घटक कुछ ऐसा कर सकता है जो समय में रेफरी को संलग्न करने से रोकता है।

उदाहरण के लिए, शायद यह बच्चे को वापस नहीं कर रहा है render(), और इसके बजाय ReactDOM.render()एक जीवन चक्र हुक में बुला रहा है। आप इस का एक उदाहरण पा सकते हैं यहाँ । उस उदाहरण में, हम प्रस्तुत करते हैं:

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

लेकिन अपने जीवन चक्र विधि में MyModalएक ReactDOM.render()कॉल करता है : componentDidUpdate

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

render() {
  return null;
}

रिएक्ट 16 के बाद से, एक जीवनचक्र के दौरान इस तरह के शीर्ष-स्तर के रेंडर कॉल विलंबित होंगे जब तक कि जीवनचक्र पूरे पेड़ के लिए नहीं चलता । यह बताएगा कि आप समय में संलग्न रेफरी क्यों नहीं देख रहे हैं।

इस समस्या का हल नेस्टेड कॉल के बजाय पोर्टल का उपयोग ReactDOM.renderकरना है:

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

इस तरह हमारे <div>रेफरी के साथ वास्तव में रेंडर आउटपुट में शामिल है।

इसलिए यदि आप इस समस्या का सामना करते हैं, तो आपको यह सत्यापित करने की आवश्यकता है कि आपके घटक और रेफरी के बीच कुछ भी नहीं है जो बच्चों को प्रदान करने में देरी कर सकता है।

setStateरेफरी को स्टोर करने के लिए उपयोग न करें

सुनिश्चित करें कि आप setStateरिफ कॉलबैक में रेफरी को स्टोर करने के लिए उपयोग नहीं कर रहे हैं , क्योंकि यह एसिंक्रोनस है और "समाप्त" होने से पहले इसे पहले componentDidMountनिष्पादित किया जाएगा।


फिर भी एक मुद्दा?

यदि ऊपर दिए गए सुझावों में से कोई भी मदद नहीं करता है, तो प्रतिक्रिया में एक समस्या दर्ज करें और हम देखेंगे।


2
मैंने इस स्थिति को भी स्पष्ट करने के लिए अपने उत्तर को संपादित किया है। प्रथम खंड देखें। उम्मीद है की यह मदद करेगा!
दान अब्रामोव

हाय @DanAbramov इसके लिए धन्यवाद! दुर्भाग्य से जब मैं पहली बार सामना किया था तो मैं एक प्रतिलिपि प्रस्तुत करने योग्य मामला विकसित करने में असमर्थ था। अफसोस की बात है कि मैं अब उस प्रोजेक्ट पर काम नहीं कर रहा हूं और तब से रिप्रेजेंट नहीं कर पा रहा हूं। हालांकि यह सवाल काफी लोकप्रिय हो गया है कि मैं सहमत हूं, प्रतिलिपि प्रस्तुत करने योग्य मामले को खोजने की कोशिश करना महत्वपूर्ण है क्योंकि कई लोग इस मुद्दे का अनुभव कर रहे हैं।
क्विकशिफ्टिन

मुझे लगता है कि कई मामलों में यह गलतफहमी के कारण होने की संभावना है। प्रतिक्रिया 15 में यह एक त्रुटि के कारण भी हो सकता है जो निगल गया (प्रतिक्रिया 16 में बेहतर त्रुटि से निपटने और इसे रोकता है)। जब ऐसा होता है तो अधिक मामलों की समीक्षा करने में मुझे खुशी होती है, इसलिए बेझिझक उन्हें टिप्पणियों में जोड़ें।
दान अब्रामोव

मदद करता है! मैं वास्तव में ध्यान नहीं दिया कि वहाँ एक पूर्व लोडर था।
Nazariy

1
इस जवाब ने वास्तव में मेरी मदद की। मैं कुछ खाली "Refs" संदर्भ के साथ संघर्ष कर रहा था, और अच्छी तरह से, यह "तत्वों" को प्रदान नहीं किया गया था।
मार्कसेफ

1

समस्या का एक अलग अवलोकन।

मैंने महसूस किया है कि विकास मोड में रहते हुए ही समस्या हुई। अधिक जांच के बाद, मैंने पाया कि react-hot-loaderमेरे वेबपैक कॉन्फ़िगरेशन में अक्षम करना इस समस्या को रोकता है।

मै इस्तेमाल कर रहा हूँ

  • "प्रतिक्रिया-हॉट-लोडर": "3.1.3"
  • "वेबपैक": "4.10.2",

और यह एक इलेक्ट्रॉन ऐप है।

मेरा आंशिक वेबपैक विकास विन्यास

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

यह संदिग्ध हो गया जब मैंने देखा कि रेंडर में इनलाइन फ़ंक्शन का उपयोग करना () काम कर रहा था, लेकिन एक बाध्य पद्धति का उपयोग करना दुर्घटनाग्रस्त हो गया था।

किसी भी मामले में काम करता है

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

प्रतिक्रिया-गर्म-लोडर के साथ क्रैश (रेफरी घटक में अपरिभाषित है)

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

  inputRef (input) {
    this.inputField = input
  }

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

ईमानदार होने के लिए, गर्म रील को अक्सर "सही" पाने के लिए समस्याग्रस्त किया गया है। देव उपकरण तेजी से अद्यतन करने के साथ, हर परियोजना का एक अलग विन्यास होता है। हो सकता है कि मेरा विशेष कॉन्फ़िगरेशन ठीक हो जाए। अगर ऐसा है तो मैं आपको यहां बता दूंगा।


यह समझा सकता है कि मुझे CodePen में इसके साथ समस्या क्यों हो रही है, लेकिन इनलाइन फ़ंक्शन का उपयोग करने से मेरे मामले में मदद नहीं मिली है।
22

0

समस्या तब भी उत्पन्न हो सकती है जब आप किसी अनमाउंट किए गए घटक के रेफ का उपयोग करने की कोशिश करते हैं जैसे कि सेटर्निवल में रेफरी का उपयोग करें और घटक अनमाउंट के दौरान सेट अंतराल को स्पष्ट न करें।

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

उदाहरण के लिए हमेशा स्पष्ट अंतराल,

componentWillUnmount(){
    clearInterval(interval_holder)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.