क्या एसिंक्स कंपोनेंटडिमाउंट () का उपयोग अच्छा है?


139

componentDidMount()अभिक्रिया मूल में एक async फ़ंक्शन अच्छा अभ्यास के रूप में उपयोग कर रहा है या मुझे इससे बचना चाहिए?

AsyncStorageजब घटक की गणना होती है, तो मुझे कुछ जानकारी प्राप्त करने की आवश्यकता होती है , लेकिन जिस तरह से मुझे पता है कि यह संभव है कि componentDidMount()फ़ंक्शन को async बनाना है ।

async componentDidMount() {
    let auth = await this.getAuth();
    if (auth) 
        this.checkAuth(auth);
}

क्या इसके साथ कोई समस्या है और क्या इस समस्या का कोई अन्य समाधान है?


1
"अच्छा अभ्यास" एक राय का विषय है। क्या यह काम करता है? हाँ।
क्रि।

2
यहाँ एक अच्छा लेख है जो दिखाता है कि क्यों async प्रतीक्षारत hackernoon.com/
शुभम खत्री

बस redux-thunk का उपयोग करें यह समस्या को हल करेगा
तिलक मैडी

@TilakMaddy आप क्यों मानते हैं कि प्रत्येक प्रतिक्रिया ऐप redux का उपयोग करता है?
मीराकुन

@ मिरकुरुन ने स्टैक ओवरफ्लो का पूरा अनुमान क्यों लगाया कि मैं jQuery का उपयोग करता हूं जब मैं दिन में वापस सादे जावास्क्रिप्ट प्रश्न पूछता था?
तिलक मैडी

जवाबों:


162

आइए मतभेदों को इंगित करके और यह निर्धारित करें कि यह कैसे परेशानी पैदा कर सकता है।

यहाँ async और "सिंक" componentDidMount()जीवन-चक्र विधि का कोड है:

// This is typescript code
componentDidMount(): void { /* do something */ }

async componentDidMount(): Promise<void> {
    /* do something */
    /* You can use "await" here */
}

कोड को देखकर, मैं निम्नलिखित अंतर बता सकता हूं:

  1. asyncकीवर्ड: टाइपप्रति में, यह महज एक कोड मार्कर है। यह 2 चीजें करता है:
    • वापसी प्रकार के लिए मजबूर होने की Promise<void>बजाय void। यदि आप स्पष्ट रूप से रिटर्न प्रकार को गैर-वादा (उदा: शून्य) निर्दिष्ट करते हैं, तो टाइपस्क्रिप्ट आप पर एक त्रुटि छोड़ेगा।
    • आपको awaitविधि के अंदर कीवर्ड का उपयोग करने की अनुमति देता है ।
  2. वापसी प्रकार से बदल दिया जाता voidहैPromise<void>
    • इसका मतलब है कि अब आप ऐसा कर सकते हैं:
      async someMethod(): Promise<void> { await componentDidMount(); }
  3. अब आप awaitविधि के अंदर कीवर्ड का उपयोग कर सकते हैं और अस्थायी रूप से इसके निष्पादन को रोक सकते हैं । ऐशे ही:

    async componentDidMount(): Promise<void> {
        const users = await axios.get<string>("http://localhost:9001/users");
        const questions = await axios.get<string>("http://localhost:9001/questions");
    
        // Sleep for 10 seconds
        await new Promise(resolve => { setTimeout(resolve, 10000); });
    
        // This line of code will be executed after 10+ seconds
        this.setState({users, questions});
        return Promise.resolve();
    }

अब, वे मुसीबतों का कारण कैसे बन सकते हैं?

  1. asyncकीवर्ड बिल्कुल हानिरहित है।
  2. मैं किसी भी स्थिति की कल्पना नहीं कर सकता, जिसमें आपको componentDidMount()विधि के लिए कॉल करने की आवश्यकता हो ताकि रिटर्न प्रकार Promise<void>भी हानिरहित हो।

    Promise<void>बिना awaitकीवर्ड के वापसी प्रकार वाली पद्धति पर कॉल करने से किसी प्रकार के रिटर्न वाले कॉल से कोई अंतर नहीं पड़ेगा void

  3. चूंकि componentDidMount()इसके निष्पादन में देरी के बाद कोई जीवन-चक्र विधियां नहीं हैं, इसलिए यह बहुत सुरक्षित लगता है। लेकिन एक गोत्र है।

    मान लीजिए, ऊपर this.setState({users, questions});10 सेकंड के बाद निष्पादित किया जाएगा। देरी के समय के बीच में, एक और ...

    this.setState({users: newerUsers, questions: newerQuestions});

    ... सफलतापूर्वक निष्पादित किया गया और DOM अपडेट किया गया। परिणाम उपयोगकर्ताओं के लिए दिखाई दे रहे थे। घड़ी की टिक टिक जारी रही और 10 सेकंड बीत गए। विलंबित this.setState(...)तब निष्पादित होगा और पुराने उपयोगकर्ताओं और पुराने प्रश्नों के साथ DOM को फिर से अपडेट किया जाएगा। इसका परिणाम उपयोगकर्ताओं को भी दिखाई देगा।

=> यह विधि के asyncसाथ उपयोग करने के लिए बहुत सुरक्षित है (मैं 100% के बारे में निश्चित नहीं हूं) componentDidMount()। मैं इसका बहुत बड़ा प्रशंसक हूं और अभी तक मुझे ऐसे किसी भी मुद्दे का सामना नहीं करना पड़ा है जो मुझे बहुत अधिक सिरदर्द देता हो।


जब आप उस मुद्दे के बारे में बात करते हैं जहां एक और सेटस्टैट लंबित वादे से पहले हुआ था, तो क्या यह एसेंश के बिना समान नहीं है / सिंटैक्टिक शुगर या यहां तक ​​कि क्लासिक कॉलबैक का इंतजार है?
क्लाफौ

3
हाँ! setState()हमेशा एक छोटे से जोखिम का सामना करना पड़ता है । हमें देखभाल के साथ आगे बढ़ना चाहिए।
C

मुझे लगता है कि समस्याओं से बचने का एक तरीका यह है कि isFetching: trueकिसी घटक की स्थिति के अंदर कुछ का उपयोग किया जाए । मैंने केवल इसे redux के साथ उपयोग किया है, लेकिन मुझे लगता है कि यह केवल प्रतिक्रिया प्रबंधन के साथ पूरी तरह से मान्य है। हालांकि यह वास्तव में उसी राज्य की समस्या को हल नहीं किया जा रहा है जो कोड में कहीं और अपडेट किया गया है ...
Clafou

1
मैं इस से सहमत हूँ। वास्तव में, isFetchingझंडा समाधान बहुत आम है, खासकर जब हम बैक-एंड प्रतिक्रिया ( isFetching: true) का इंतजार करते हुए फ्रंट-एंड में कुछ एनिमेशन खेलना चाहते हैं ।
Hiếu

3
यदि आपको समस्याएं आ सकते हैं यदि आप घटक के बाद setState कर अनमाउंट किया है
एलीएज़र Steinbock

18

अद्यतन अप्रैल 2020: यह मुद्दा नवीनतम रिएक्ट 16.13.1 में तय किया गया है, यह सैंडबॉक्स उदाहरण देखें । इसे इंगित करने के लिए @abernier को धन्यवाद।


मैंने कुछ शोध किए हैं, और मुझे एक महत्वपूर्ण अंतर मिला है: रिएक्ट async जीवन चक्र विधियों से त्रुटियों को संसाधित नहीं करता है।

तो, अगर आप ऐसा कुछ लिखते हैं:

componentDidMount()
{
    throw new Error('I crashed!');
}

तब आपका त्रुटि त्रुटि सीमा से पकड़ा जाएगा , और आप इसे संसाधित कर सकते हैं और एक सुंदर संदेश प्रदर्शित कर सकते हैं।

यदि हम इस तरह कोड बदलते हैं:

async componentDidMount()
{
    throw new Error('I crashed!');
}

जो इस के बराबर है:

componentDidMount()
{
    return Promise.reject(new Error('I crashed!'));
}

तब आपकी त्रुटि चुपचाप निगल ली जाएगी । आप पर शर्म आती है, प्रतिक्रिया ...

तो, हम त्रुटियों को कैसे संसाधित करते हैं? एकमात्र तरीका इस तरह स्पष्ट रूप से पकड़ा जाना प्रतीत होता है:

async componentDidMount()
{
    try
    {
         await myAsyncFunction();
    }
    catch(error)
    {
        //...
    }
}

या इस तरह:

componentDidMount()
{
    myAsyncFunction()
    .catch(()=>
    {
        //...
    });
}

यदि हम अभी भी अपनी त्रुटि को त्रुटि सीमा तक पहुँचाना चाहते हैं, तो मैं निम्नलिखित चाल के बारे में सोच सकता हूँ:

  1. त्रुटि को पकड़ें, त्रुटि हैंडलर को घटक स्थिति बदलें
  2. यदि राज्य किसी त्रुटि को इंगित करता है, तो इसे renderविधि से फेंक दें

उदाहरण:

class BuggyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  buggyAsyncfunction(){ return Promise.reject(new Error('I crashed async!'));}

  async componentDidMount() {
    try
    {
      await this.buggyAsyncfunction();
    }
    catch(error)
    {
        this.setState({error: error});
    }
  }

  render() {
    if(this.state.error)
        throw this.state.error;

    return <h1>I am OK</h1>;
  }
}

क्या इसके लिए कोई समस्या बताई गई है? यह रिपोर्ट करने के लिए उपयोगी हो सकता है अगर अभी भी मामला ... thx
abernier

@abernier मुझे लगता है कि यह पद से है ... हालांकि शायद वे इसे सुधार सकते हैं। मैंने इस बारे में कोई भी मामला दर्ज नहीं किया ...
CF

1
ऐसा लगता है कि अब ऐसा नहीं है, कम से कम रिएक्ट 16.13.1 के साथ यहां परीक्षण किया गया: codeandbox.io/s/bold-ellis-n1cid?file=/src/App.js
abernier

9

आपका कोड ठीक है और मेरे लिए बहुत पठनीय है। इस डेल जेफरसन के लेख को देखें जहां वह एक async componentDidMountउदाहरण दिखाता है और साथ ही वास्तव में अच्छा दिखता है।

लेकिन कुछ लोग कहेंगे कि कोड पढ़ने वाला व्यक्ति यह मान सकता है कि रिएक्ट लौटे वादे के साथ कुछ करता है।

तो इस कोड की व्याख्या और अगर यह एक अच्छा अभ्यास है या नहीं, तो यह बहुत ही व्यक्तिगत है।

यदि आप एक और समाधान चाहते हैं, तो आप वादों का उपयोग कर सकते हैं । उदाहरण के लिए:

componentDidMount() {
    fetch(this.getAuth())
      .then(auth => {
          if (auth) this.checkAuth(auth)
      })
}

3
... या यह भी, बस अंदर के asyncसाथ एक इनलाइन फ़ंक्शन का उपयोग करें await...?
एरिक कपलुन

यह भी एक विकल्प @ErikAllik :)
Tiago Alves

@ErikAllik क्या आपके पास एक उदाहरण है?
पाब्लो रिनकोन

1
@PabloRincon (async () => { const data = await fetch('foo'); const result = await submitRequest({data}); console.log(result) })()जहां fetchऔर जैसे submitRequestवादे हैं जो वादे वापस करते हैं।
एरिक कपलुन

यह कोड निश्चित रूप से खराब है, क्योंकि यह getAuth फ़ंक्शन में हुई किसी भी त्रुटि को निगल जाएगा। और यदि फ़ंक्शन नेटवर्क के साथ कुछ करता है (उदाहरण के लिए), त्रुटियों की उम्मीद की जानी चाहिए।
CF

6

जब आप componentDidMountबिना asyncकीवर्ड का उपयोग करते हैं , तो डॉक्टर यह कहता है:

आप तुरंत फोन कर सकते हैं। यह एक अतिरिक्त रेंडरिंग को ट्रिगर करेगा, लेकिन यह ब्राउज़र द्वारा स्क्रीन को अपडेट करने से पहले होगा।

यदि आप उपयोग करते async componentDidMountहैं तो आप इस क्षमता को ढीला कर देंगे: एक और रेंडर यह होगा कि ब्राउज़र के बाद स्क्रीन को अपडेट करें। लेकिन imo, यदि आप async का उपयोग करने के बारे में सोच रहे हैं, जैसे डेटा लाना, तो आप बच नहीं सकते हैं ब्राउज़र स्क्रीन को दो बार अपडेट करेगा। किसी अन्य दुनिया में, ब्राउज़र को स्क्रीन अपडेट करने से पहले घटक को दबाना संभव नहीं है


1
मुझे यह उत्तर पसंद है क्योंकि यह संक्षिप्त है और डॉक्स द्वारा समर्थित है। क्या आप कृपया उन डॉक्स का लिंक जोड़ सकते हैं जिन्हें आप संदर्भित कर रहे हैं।
TheUtherSide

यह भी एक अच्छी बात हो सकती है जैसे यदि आप संसाधन लोड करते समय लोडिंग स्थिति प्रदर्शित कर रहे हैं और तब सामग्री जब यह किया जाता है।
हुजुल

3

अपडेट करें:

(मेरी बिल्ड: रिएक्ट 16, वेबपैक 4, बैबल 7):

Babel 7 का उपयोग करते समय आप पाएंगे:

इस पैटर्न का उपयोग ...

async componentDidMount() {
    try {
        const res = await fetch(config.discover.url);
        const data = await res.json();
        console.log(data);
    } catch(e) {
        console.error(e);
    }
}

आप निम्न त्रुटि में चलेंगे ...

बिना संदर्भ के संदर्भ: पुनर्योजी पुन: परिभाषित नहीं किया गया है

इस मामले में आपको बेबल-प्लगइन-ट्रांसफॉर्म-रनटाइम इंस्टॉल करना होगा

https://babeljs.io/docs/en/babel-plugin-transform-runtime.html

यदि किसी कारण से आप उपरोक्त पैकेज (बैबल-प्लगइन-ट्रांसफॉर्म-रनटाइम) स्थापित नहीं करना चाहते हैं, तो आप वादा पैटर्न से चिपके रहना चाहेंगे ...

componentDidMount() {
    fetch(config.discover.url)
    .then(res => res.json())
    .then(data => {
        console.log(data);
    })
    .catch(err => console.error(err));
}

3

मुझे लगता है कि यह तब तक ठीक है जब तक आप जानते हैं कि आप क्या कर रहे हैं। लेकिन यह भ्रामक हो सकता है क्योंकि async componentDidMount()चलने के बाद भी चल सकता है componentWillUnmountऔर घटक अनमाउंट हो गया है।

तुम भी तुल्यकालिक और अतुल्यकालिक दोनों कार्यों को अंदर शुरू करना चाह सकते हैं componentDidMount। यदि componentDidMountएसिंक्स था, तो आपको पहले सभी सिंक्रोनस कोड डालने होंगे await। यह किसी के लिए स्पष्ट नहीं हो सकता है कि पहले कोड awaitतुल्यकालिक रूप से चलता है। इस मामले में, मैं शायद componentDidMountतुल्यकालिक रखूंगा, लेकिन क्या इसमें सिंक और एसिंक्स तरीके हैं।

आप चुनते हैं async componentDidMount()बनाम सिंक componentDidMount()बुला asyncतरीकों, क्या आप वाकई किसी भी श्रोताओं या async तरीकों है कि अभी भी जब घटक unmounts चला रहे हो सकते को साफ करना है।


2

दरअसल, ComponentDidMount में async लोडिंग एक अनुशंसित डिज़ाइन पैटर्न है क्योंकि React लीगेसी जीवनचक्र विधियों ( ComponentsWillMount, ComponentsWillReceiveProps, ComponentsWillUpdate ) और Async रेंडरिंग से दूर जाता है।

यह ब्लॉग पोस्ट यह समझाने में बहुत मददगार है कि यह सुरक्षित क्यों है और Comp घटकDidMount में async लोडिंग के लिए उदाहरण प्रदान करता है:

https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html


3
Async रेंडरिंग का वास्तव में जीवनचक्र को स्पष्ट रूप से async बनाने से कोई लेना-देना नहीं है। यह वास्तव में एक विरोधी पैटर्न है। अनुशंसित समाधान वास्तव में एक जीवन पद्धति विधि से एक async विधि को कॉल करना है
क्लेटन रे

1

मुझे कुछ इस तरह का उपयोग करना पसंद है

componentDidMount(){
   const result = makeResquest()
}
async makeRequest(){
   const res = await fetch(url);
   const data = await res.json();
   return data
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.