2017-07-25 38 views
6

सरलीकृत समस्या का कारण बनता है। एक वादा के अंदर this.setState को कॉल करना, वादे लंबित समाप्त होने से पहले प्रस्तुत करता है।वाइस के अंदर यहसेट अजीब व्यवहार अजीब व्यवहार

मेरे समस्याएं हैं:

  1. this.setState, immediatly
    • लौटे मैं इसे async होने की उम्मीद नहीं है, ताकि लंबित वादा पहले बंद कर दिया जाएगा।
  2. यदि रेंडर फ़ंक्शन के अंदर कुछ तोड़ जाएगा, तो वादा के अंदर पकड़ को बुलाया जाता है।
    • शायद 1 जैसा ही मुद्दा हो सकता है कि ऐसा लगता है कि प्रस्तुत करना अभी भी उस वादे के संदर्भ में है जिसमें यह .setState कहा जाता था।

import dummydata_rankrequests from "../dummydata/rankrequests"; 
class RankRequestList extends Component { 

    constructor(props) { 
    super(props); 

    this.state = { loading: false, data: [], error: null }; 

    this.makeRankRequestCall = this.makeRankRequestCall.bind(this); 
    this.renderItem = this.renderItem.bind(this); 
    } 

    componentDidMount() { 

    // WORKS AS EXPECTED 
    // console.log('START set'); 
    // this.setState({ data: dummydata_rankrequests.data, loading: false }); 
    // console.log('END set'); 

    this.makeRankRequestCall() 
    .then(done => { 
     // NEVER HERE 
     console.log("done"); 
    });  
    } 

    makeRankRequestCall() { 
    console.log('call makeRankRequestCall'); 
    try { 
     return new Promise((resolve, reject) => { 
     resolve(dummydata_rankrequests); 
     }) 
     .then(rankrequests => { 
     console.log('START makeRankRequestCall-rankrequests', rankrequests); 
     this.setState({ data: rankrequests.data, loading: false }); 
     console.log('END _makeRankRequestCall-rankrequests'); 
     return null; 
     }) 
     .catch(error => { 
     console.log('_makeRankRequestCall-promisecatch', error); 
     this.setState({ error: RRError.getRRError(error), loading: false }); 
     }); 
    } catch (error) { 
     console.log('_makeRankRequestCall-catch', error); 
     this.setState({ error: RRError.getRRError(error), loading: false }); 
    } 
    } 

    renderItem(data) { 
    const height = 200; 
    // Force a Unknown named module error here 
    return (
     <View style={[styles.item, {height: height}]}> 
     </View> 
    ); 
    } 

    render() { 
    let data = []; 
    if (this.state.data && this.state.data.length > 0) { 
     data = this.state.data.map(rr => { 
     return Object.assign({}, rr); 
     }); 
    } 
    console.log('render-data', data); 
    return (
     <View style={styles.container}> 
     <FlatList style={styles.listContainer1} 
      data={data} 
      renderItem={this.renderItem} 
     /> 
     </View> 
    ); 
    } 
} 

currrent लॉग दिखाता है:

  • प्रस्तुत करना-डेटा, []
  • स्टार्ट makeRankRequestCall-rankrequests
  • प्रस्तुत करना-डेटा, [। ..]
  • _makeRankRequestCall-promisecatch त्रुटि: अज्ञात नामित मॉड्यूल ...
  • प्रस्तुत करना-डेटा, [...]
  • संभव बिना क्रिया वादा

एंड्रॉयड एम्यूलेटर "प्रतिक्रिया": "16.0.0- alpha.12 ", " प्रतिक्रिया देशी ":" 0.46.4 ",

संपादित: this.setState आसपास रैपिंग setTimeout भी

काम करता है
setTimeout(() => { 
     this.setState({ data: respData.data, loading: false }); 
    }, 1000); 

EDIT2: समानांतर https://github.com/facebook/react-native/issues/15214

+0

मैं मुसीबत बिल्कुल इसलिए जो आप कोशिश कर रहे हैं समस्या आ रही है समाधान करना। क्या आप 'console.log (" done ") के समय केवल एक बार फिर से प्रस्तुत करने का प्रयास कर रहे हैं;' निष्पादित किया गया है? यदि ऐसा है, तो इसे प्राप्त करने का एक तरीका 'चाहिएComponentUpdate() 'को ओवरराइट करना होगा ताकि जब आप पुनः प्रस्तुत करने के लिए तैयार हों तो यह हमेशा' झूठा 'लौटाता है, और' this.force अद्यतन करें '। https://facebook.github.io/react/docs/react-component.html#forceupdate – therobinkim

उत्तर

1

दोनों Promise और this.setState() जावास्क्रिप्ट में अतुल्यकालिक हैं में प्रतिक्रिया देशी GitHub में एक बग रिपोर्ट बनाई। कहें, यदि आपके पास निम्न कोड है:

console.log(a); 
networkRequest().then(result => console.log(result)); // networkRequest() is a promise 
console.log(b); 

ए और बी नेटवर्क अनुरोध के परिणामस्वरूप पहले मुद्रित हो जाएगा।

इसी तरह, this.setState() भी तो अतुल्यकालिक है, यदि आप this.setState() पूर्ण होने के बाद कुछ निष्पादित करने के लिए चाहते हैं, आप के रूप में यह सब करने की ज़रूरत:

this.setState({data: rankrequests.data},() => { 
    // Your code that needs to run after changing state 
}) 

प्रतिक्रिया पुन: renders हर बार this.setState() निष्पादित हो जाता है, इसलिए आप कर रहे हैं पूरे वादे को हल करने से पहले अपना घटक अपडेट कर रहा है।

async componentDidMount() { 
    let rankrequests; 
    try { 
    rankrequests = await this.makeRankRequestCall() // result contains your data 
    } catch(error) { 
    console.error(error); 
    } 
    this.setState({ data: rankrequests.data, loading: false },() => { 
    // anything you need to run after setting state 
    }); 
} 

आशा है कि यह मदद करता है: यह समस्या async समारोह के रूप में अपने componentDidMount() बनाने और का उपयोग करके हल किया जा सकता वादा हल करने का इंतजार है।

+0

मैं किसी दिए गए घटक के आरएन-लाइफसाइकिल विधियों के रूप में महत्वपूर्ण कुछ बदलने की सलाह नहीं दूंगा। इसे एसिंक बनाना एक बड़ी गलती हो सकती है। – GoreDefex

+0

इसके अलावा, यदि आप किसी राज्य अपडेट के जवाब में कुछ भी कर रहे हैं तो आपको आरएन-लाइफसाइकिल विधि 'घटकडिड अपडेट' का उपयोग करना चाहिए – GoreDefex

1

मुझे भी यह समझने में कठिनाई हो रही है कि आप यहां क्या करने का प्रयास कर रहे हैं, इसलिए मैंने इसमें एक स्टैब लिया।

चूंकि this.setState() विधि एक रेंडर ट्रिगर करने का इरादा है, मैं इसे तब तक कॉल नहीं करता जब तक कि आप प्रस्तुत करने के लिए तैयार न हों। आप राज्य परिवर्तनीय पर अद्यतित होने पर निर्भर हैं और इच्छानुसार उपयोग/कुशलतापूर्वक उपयोग करने में सक्षम हैं। this.state. वैरिएबल का अनुमानित व्यवहार, प्रस्तुत करने के समय तैयार होना है। मुझे लगता है कि आपको एक और अधिक परिवर्तनीय चर का उपयोग करने की आवश्यकता है जो राज्यों और प्रस्तुतकर्ताओं से बंधी नहीं है। जब आप समाप्त कर लेंगे, और केवल तभी, आपको प्रतिपादन करना चाहिए।

यहाँ दिखाने के लिए इस विचार करेंगे अपने कोड को फिर से काम किया है: "../dummydata/rankrequests" से

आयात dummydata_rankrequests;

वर्ग RankRequestList फैली घटक {

constructor(props) { 
    super(props); 

    /* 
     Maybe here is a good place to model incoming data the first time? 
     Then you can use that data format throughout and remove the heavier modelling 
     in the render function below 

     if (this.state.data && this.state.data.length > 0) { 
      data = this.state.data.map(rr => { 
       return Object.assign({}, rr); 
      }); 
     } 
    */ 

    this.state = { 
     error: null, 
     loading: false, 
     data: (dummydata_rankrequests || []), 
    }; 

    //binding to 'this' context here is unnecessary 
    //this.makeRankRequestCall = this.makeRankRequestCall.bind(this); 
    //this.renderItem = this.renderItem.bind(this); 
} 


componentDidMount() { 
    // this.setState({ data: dummydata_rankrequests.data, loading: false }); 

    //Context of 'this' is already present in this lifecycle component 
    this.makeRankRequestCall(this.state.data).then(returnedData => { 
     //This would have no reason to be HERE before, you were not returning anything to get here 
     //Also, 
     //should try not to use double quotes "" in Javascript 


     //Now it doesn't matter WHEN we call the render because all functionality had been returned and waited for 
     this.setState({ data: returnedData, loading: false }); 

    }).catch(error => { 
     console.log('_makeRankRequestCall-promisecatch', error); 
     this.setState({ error: RRError.getRRError(error), loading: false }); 
    }); 
} 


//I am unsure why you need a bigger call here because the import statement reads a JSON obj in without ASync wait time 
//...but just incase you need it... 
async makeRankRequestCall(currentData) { 
    try { 
     return new Promise((resolve, reject) => { 
      resolve(dummydata_rankrequests); 

     }).then(rankrequests => { 
      return Promise.resolve(rankrequests); 

     }).catch(error => { 
      return Promise.reject(error); 
     }); 

    } catch (error) { 
     return Promise.reject(error); 
    } 
} 


renderItem(data) { 
    const height = 200; 

    //This is usually where you would want to use your data set 
    return (
     <View style={[styles.item, {height: height}]} /> 
    ); 

    /* 
     //Like this 
     return { 
      <View style={[styles.item, {height: height}]}> 
       { data.item.somedataTitleOrSomething } 
      </View> 
     }; 
    */ 
} 


render() { 
    let data = []; 

    //This modelling of data on every render will cause a huge amount of heaviness and is not scalable 
    //Ideally things are already modelled here and you are just using this.state.data 
    if (this.state.data && this.state.data.length > 0) { 
     data = this.state.data.map(rr => { 
      return Object.assign({}, rr); 
     }); 
    } 
    console.log('render-data', data); 

    return (
     <View style={styles.container}> 
      <FlatList 
       data={data} 
       style={styles.listContainer1} 
       renderItem={this.renderItem.bind(this)} /> 
      { /* Much more appropriate place to bind 'this' context than above */ } 
     </View> 
    ); 
} 

}

1

setState अतुल्यकालिक वास्तव में है। मुझे लगता है कि makeRankRequestCall इस तरह होना चाहिए:

async makeRankRequestCall() { 
    console.log('call makeRankRequestCall'); 
    try { 
    const rankrequests = await new Promise((resolve, reject) => { 
     resolve(dummydata_rankrequests); 
    }); 

    console.log('START makeRankRequestCall-rankrequests', rankrequests); 
    this.setState({ data: rankrequests.data, loading: false }); 
    console.log('END _makeRankRequestCall-rankrequests'); 
    } catch(error) { 
    console.log('_makeRankRequestCall-catch', error); 
    this.setState({ error: RRError.getRRError(error), loading: false }); 
    } 
} 

दूसरे, वादा renderItem की एक त्रुटि को पकड़ने बिल्कुल ठीक है। जावास्क्रिप्ट में, किसी भी कैच ब्लॉक में कोड में कहीं भी फेंकने वाली किसी भी त्रुटि को पकड़ लिया जाएगा। specs के अनुसार:

The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.

तो क्रम में, यह तय करने के लिए अगर आप विफल renderItem उम्मीद करते हैं, तुम कर सकते हो निम्नलिखित:

renderItem(data) { 
    const height = 200; 
    let item = 'some_default_item'; 
    try { 
    // Force a Unknown named module error here 
    item = styles.item 
    } catch(err) { 
    console.log(err); 
    } 
    return (
    <View style={[item, {height: height}]}> 
    </View> 
); 
} 
संबंधित मुद्दे