ब्रेक पर युद्ध। प्रतिक्रियाशील मूल में घटक रेंडरिंग की संख्या का अनुकूलन

हेलो, हेब्र! मेरा नाम कमो स्पर्त्स्यान है, मैं Profi.ru पर रिएक्ट नेटिव डेवलपमेंट में व्यस्त हूं। यदि आप उत्पाद सुविधाओं को शीघ्रता से वितरित करने और विकास की गति पर ध्यान केंद्रित करने के लिए रिएक्ट नेटिव तकनीक का उपयोग करने का निर्णय लेते हैं , तो आपको प्रदर्शन के मुद्दों में भाग लेने की संभावना है। कम से कम हमारे साथ तो यही हुआ। छह महीने के सक्रिय विकास के बाद, हमारे आवेदन का प्रदर्शन एक महत्वपूर्ण स्तर से नीचे गिर गया - सब कुछ बेतहाशा धीमा था। इसलिए, हमने अनुकूलन किया - स्टार्टअप के दौरान सभी "ब्रेक" को हटा दिया, स्क्रीन के बीच संक्रमण, स्क्रीन प्रदान करना, उपयोगकर्ता के कार्यों पर प्रतिक्रिया। नतीजतन, तीन महीनों में वे उपयोगकर्ता के अनुभव को मूल स्तर पर लाए। इस लेख में मैं बात करना चाहता हूं कि हमने रिएक्ट नेटिव पर एप्लिकेशन को कैसे अनुकूलित किया और कई घटक रेंडरर्स की समस्या को हल किया।



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

हम Redux के साथ युग्मित रिएक्टिव नेटिव का उपयोग करते हैं। कुछ सुझाव इस पुस्तकालय से संबंधित हैं। इसके अलावा उदाहरण में, मैं Redux-thunk लाइब्रेरी का उपयोग करता हूं - नेटवर्क के साथ काम करने के लिए अनुकरण करने के लिए।

प्रदर्शन के बारे में कब सोचना है?


वास्तव में, यह आवेदन पर काम की शुरुआत से याद रखने योग्य है। लेकिन अगर आपका आवेदन पहले से धीमा हो रहा है - निराशा न करें, तो सब कुछ तय किया जा सकता है।

हर कोई जानता है, लेकिन सिर्फ मामले में, मैं उल्लेख करूंगा: कमजोर उपकरणों पर प्रदर्शन की जांच करना बेहतर है। यदि आप शक्तिशाली उपकरणों का विकास कर रहे हैं, तो आपको अंतिम उपयोगकर्ताओं के "ब्रेक" के बारे में पता नहीं हो सकता है। अपने लिए तय करें कि आपके द्वारा निर्देशित कौन से उपकरण हैं। अनुकूलन के बाद परिणामों की तुलना करने के लिए नियंत्रण भूखंडों में समय या एफपीएस को मापें

प्रतिक्रियाशील मूल निवासी डेवलपर उपकरण के माध्यम से एफपीएस अनुप्रयोगों को मापने की क्षमता प्रदान करता है → पूर्ण मॉनिटर दिखाएं। संदर्भ मूल्य 60 फ्रेम प्रति सेकंड है। यह संकेतक जितना कम होगा, उतना ही मजबूत अनुप्रयोग "धीमा" होगा - उपयोगकर्ता क्रियाओं में देरी के साथ प्रतिक्रिया या प्रतिक्रिया नहीं करता है। एफपीएस पर मुख्य प्रभावों में से एक रेंडरर्स की संख्या है जिनकी "गंभीरता" घटकों की जटिलता पर निर्भर करती है।

उदाहरण वर्णन


मैं समाचार की सूची के साथ एक साधारण आवेदन के उदाहरण पर सभी सिफारिशें दिखाता हूं। एप्लिकेशन में एक स्क्रीन है, जो FlatListसमाचार के साथ स्थित है एक समाचार आइटम एक घटक NewsItemहै जिसमें दो छोटे घटक होते हैं - शीर्षक ( NewsItemTitle) और शरीर ( NewsItemBody)। पूरा उदाहरण यहां देखा जा सकता हैपाठ में आगे विशिष्ट उदाहरणों के लिए भंडार की विभिन्न शाखाओं के लिंक हैं। रिपॉजिटरी का उपयोग पाठकों की सुविधा के लिए किया जाता है जो उदाहरणों को अधिक गहराई से जानना चाहते हैं। नीचे दिए गए रिपॉजिटरी और उदाहरणों में कोड सही होने का दावा नहीं करता है - यह केवल प्रदर्शन उद्देश्यों के लिए आवश्यक है।

नीचे, सभी घटकों को लिंक और प्रॉप्स के साथ योजनाबद्ध रूप से दिखाया गया है।


प्रत्येक घटक की रेंडर विधि में, मैंने आउटपुट को इसके बारे में अद्वितीय जानकारी के कंसोल में जोड़ा:

SCREEN
ITEM_{no}
ITEM_TITLE_{no}
ITEM_BODY_{no}

जहां {no}एक ही के कई रेंडरिंग से विभिन्न समाचार रेंडरिंग के बीच अंतर करने के लिए समाचार क्रम संख्या है।

प्रत्येक refreshसमाचार सूची पर परीक्षण के लिए, अतिरिक्त समाचार को इसकी शुरुआत में जोड़ा जाता है। उसी समय, निम्न संदेश कंसोल में प्रदर्शित होता है:

--------------[ REFRESHING ]--------------

ये रिकॉर्ड समझने में मदद करेंगे कि क्या किसी विशेष घटक में कोई समस्या है, और बाद में यह निर्धारित करने के लिए कि क्या इसे अनुकूलित करना संभव था।

यदि इसे सही ढंग से लागू किया जाता है, तो लॉन्च के बाद हमारा लॉग और कई अपडेट इस तरह दिखना चाहिए:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
--------------[ REFRESHING ]--------------
SCREEN
ITEM_4
ITEM_TITLE_4
ITEM_BODY_4

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

एक घटक का प्रतिपादन कब किया जाता है?


प्रतिक्रिया और प्रतिक्रिया मूल में, घटक प्रदान करने के लिए दो शर्तें हैं:

  1. अपने प्रॉप्स / राज्य को बदलते हुए,
  2. मूल घटक का प्रस्तुतिकरण।

एक फ़ंक्शन को एक घटक में पुनर्परिभाषित किया जा सकता है shouldComponentUpdate- यह एक इनपुट के रूप में नए प्रॉप्स और स्टेट को प्राप्त करता है और बताता है कि क्या घटक का प्रतिपादन किया जाना चाहिए। अक्सर, अनावश्यक री-रेंडरर्स से बचने के लिए, प्रॉप्स और स्टेट ऑब्जेक्ट्स की एक उथली तुलना पर्याप्त है। उदाहरण के लिए, यह मूल घटक को बदलने पर अनावश्यक रेंडरर्स को समाप्त करता है, यदि वे बाल घटक को प्रभावित नहीं करते हैं। हर बार मैन्युअल तुलना करने के लिए सतह की तुलना में नहीं लिखने के लिए, आप React.PureComponentउस घटक को इनहेरिट कर सकते हैं जो इस चेक को इनकैप्सुलेट करता है।

जब हम कनेक्ट लिंक फ़ंक्शन का उपयोग करते हैं, तो Redux लाइब्रेरी एक नया घटक बनाता है जो वैश्विक राज्य से "जुड़ा हुआ" है। इस स्थिति में परिवर्तन एक विधि को ट्रिगर करता हैmapStateToPropsजो नया सहारा देता है। अगला, पुराने और नए प्रॉप्स की तुलना शुरू होती है, भले ही घटक के रूप में घोषित किया गया हो PureComponentया नहीं।

हमारे उदाहरण में इन बारीकियों पर विचार करें।

हम घटक को NewsItemगुजरने देंगे connect, से NewsItemTitleविरासत में React.Component, और NewsItemBody- से React.PureComponent

पूर्ण उदाहरण कोड

export class NewsItemTitle extends React.Component
export class NewsItemBody extends React.PureComponent

यहां एक बोर्ड अपडेट के बाद लॉग कैसा दिखेगा:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

आप देख सकते हैं कि समाचार और शीर्षक घटक फिर से मिल गए हैं। हम उन्हें बदले में विचार करेंगे।

NewsItemका उपयोग कर घोषित किया connectएक सहारा के रूप में, इस घटक को एक पहचानकर्ता प्राप्त होता है, जिसके द्वारा यह बाद में समाचार प्राप्त करता है mapStateToProps:

const mapStateToProps = (state, ownProps) => ({
  item: state.newsMap[ownProps.itemKey],
});

चूंकि बोर्ड को अपडेट करते समय सभी समाचार फिर से डाउनलोड किए जाते हैं, ऑब्जेक्ट itemअपडेट होगा और बाद में विभिन्न मेमोरी सेल को संदर्भित करेगा। दूसरे शब्दों में, वे अलग-अलग ऑब्जेक्ट होंगे, भले ही सभी निहित फ़ील्ड समान हों। इसलिए, पिछले और नए State'ov घटक रिटर्न की तुलना false। घटक को फिर से प्रदान किया जाएगा, इस तथ्य के बावजूद कि वास्तव में डेटा नहीं बदला है।

NewsItemTitleसे विरासत में मिला है React.Component, इसलिए यह हर बार मूल घटक प्रदान किए जाने पर फिर से प्रदान किया जाता है। यह पुराने और नए प्रॉप्स के मूल्यों की परवाह किए बिना होता है।

NewsItemBodyविरासत में मिला है React.PureComponent, इसलिए यह पुराने और नए प्रॉप्स की तुलना करता है। समाचार 1 और 2 में, उनके मूल्य बराबर हैं, इसलिए घटक केवल समाचार 3 के

लिए प्रदान किया जाता है। रेंडरिंग का अनुकूलन करने के लिएNewsItemTitleबस के रूप में यह घोषणा React.PureComponentके मामले में, आपको NewsItemफ़ंक्शन को फिर से परिभाषित करना होगा shouldComponentUpdate:

shouldComponentUpdate(nextProps) {
  return !shallowEqual(this.props.item, nextProps.item);
}

पूर्ण उदाहरण कोड

यहां shallowEqualउन वस्तुओं की सतह की तुलना के लिए एक विशेषता है जो Redux प्रदान करता है। आप इस तरह लिख सकते हैं:

shouldComponentUpdate(nextProps) {
  return (
    this.props.item.title !== nextProps.item.title ||
    this.props.item.body !== nextProps.item.body
  );
}

इसके बाद हमारा लॉग कैसा दिखेगा:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3

ध्यान दें
shouldComponentUpdate NewsItem , NewsItemTitle . . NewsItemTitle - NewsItem, .

React.memo और कार्यात्मक घटक


shouldComponentUpdateएक कार्यात्मक घटक में ओवरराइड करना संभव नहीं है। लेकिन इसका मतलब यह नहीं है कि एक कार्यात्मक घटक को अनुकूलित करने के लिए, आपको इसे कक्षा एक में फिर से लिखना होगा। ऐसे मामलों के लिए, React.memo संस्मरण समारोह प्रदान किया जाता हैयह एक घटक इनपुट और एक वैकल्पिक तुलना फ़ंक्शन स्वीकार करता है areEqualजब कहा जाता है, तो यह areEqualपुराने और नए रंगमंच की सामग्री हो जाता है और तुलना के परिणाम को वापस करना चाहिए। साथ अंतर shouldComponentUpdateक्या areEqualलौटना चाहिए trueअगर रंगमंच की सामग्री के बराबर हैं, और इसके नहीं विपरीत।

उदाहरण के लिए, NewsItemTitleसंस्मरण इस तरह दिख सकता है:

areEqual(prevProps, nextProps) {
  return shallowEqual(prevProps, nextProps);
}
export OptimizedNewsItemTitle = React.memo(NewsItemTitle, areEqual)

आप उत्तीर्ण नहीं होते हैं areEqualमें React.memo, तो रंगमंच की सामग्री की एक सतही तुलना, बनाया जाएगा ताकि हमारे उदाहरण सरल किया जा सकता:

export OptimizedNewsItemTitle = React.memo(NewsItemTitle)

लैम्ब्डा प्रॉप्स में कार्य करता है


घटक घटनाओं को संसाधित करने के लिए, कार्यों को इसके प्रॉपर तक पारित किया जा सकता है। सबसे महत्वपूर्ण उदाहरण कार्यान्वयन है onPressइसके लिए अक्सर अनाम लंबो कार्यों का उपयोग किया जाता है। मान लें कि NewsItemBodyहम केवल पूर्वावलोकन दिखाना चाहते हैं, और यदि आप इस पर क्लिक करते हैं - संपूर्ण पाठ। ऐसा करने के लिए, जब हम प्रतिपादन करेंगे NewsItem, NewsItemBodyहम निम्नलिखित प्रस्ताव को पारित करेंगे:

<NewsItemBody
  ...
  onPress={() => this.props.expandBody()}
  ...
/>

जब विधि shouldComponentUpdateको NewsItemहटा दिया जाता है तो लॉग इस कार्यान्वयन के साथ कैसा दिखता है:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

समाचार निकाय 1 और 2 का प्रतिपादन किया गया है, हालांकि उनका डेटा नहीं बदला है, लेकिन NewsItemBodyहै PureComponentयह इस तथ्य के कारण है कि प्रत्येक रेंडर के लिए NewsItemप्रॉप्स onPressका मूल्य नए सिरे से बनाया गया है। तकनीकी रूप से, onPressप्रत्येक रेंडर के साथ, यह मेमोरी में एक नए क्षेत्र को इंगित करता है, इसलिए NewsItemBodyरिटर्न में प्रॉप्स की सतही तुलना झूठी है। समस्या निम्नलिखित प्रविष्टि द्वारा तय की गई है:

<NewsItemBody
  ...
  onPress={this.props.expandBody}
  ...
/>

लॉग इन करें:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

पूर्ण उदाहरण कोड

दुर्भाग्य से, किसी भी तरह से एक अनाम फ़ंक्शन हमेशा इस तरह के रिकॉर्ड के लिए एक विधि या वर्ग फ़ील्ड के रूप में फिर से लिखा जा सकता है। सबसे आम मामला तब होता है जब लैम्बडा के अंदर फंक्शन का स्कोप वैरिएबल होता है जिसमें यह घोषित किया जाता है।

हमारे उदाहरण में इस मामले पर विचार करें। सामान्य सूची से एक समाचार की स्क्रीन पर स्विच करने के लिए, हम समाचार के मुख्य भाग पर क्लिक करने की प्रक्रिया को जोड़ते हैं। renderItemघटक विधि FlatListइस तरह दिखाई देगी:

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={() => this.onItemBodyPress(item)}
  />
);

एक अनाम फ़ंक्शन onBodyPressको एक वर्ग में घोषित नहीं किया जा सकता है, क्योंकि तब itemकिसी विशेष समाचार पर जाने के लिए आवश्यक चर गुंजाइश से गायब हो जाएगा

समस्या का सबसे सरल समाधान onBodyPressघटक प्रॉप्स के हस्ताक्षर को बदलना है, NewsItemताकि आवश्यक पैरामीटर को फ़ंक्शन के पास भेजा जाए। इस मामले में, यह समाचार पहचानकर्ता है।

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={item => this.onItemBodyPress(item)}
  />
);

इस मामले में, हम पहले से ही घटक वर्ग विधि में अनाम फ़ंक्शन को निकाल सकते हैं।

const renderItem = ({item}) => (
  <NewsItem
    itemKey={item}
    onBodyPress={this.onItemBodyPress}
  />
);

हालांकि, इस तरह के समाधान से हमें घटक को बदलने की आवश्यकता होगी NewsItem

class NewsItemComponent extends React.Component {
render() {
  ...
  return (
      ...
      <NewsItemBody
        ...
        onPress={() => this.props.onBodyPress(this.props.item)}
        ...
      />
      ...
  );
}

और फिर से हम संकेतित समस्या पर लौटते हैं - हम माता-पिता के प्रत्येक रेंडर के लिए बच्चे के घटक के लिए एक नया लैम्ब्डा फ़ंक्शन पास करते हैं। केवल अब हम एक स्तर नीचे चले गए हैं। लॉग इन करें:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

रूट पर इस समस्या से छुटकारा पाने के लिए, आप उपयोग कर सकते हैंकैलबैक हुक यह एक तर्क को पारित करने के साथ एक फ़ंक्शन कॉल को याद रखने की अनुमति देता है। यदि फ़ंक्शन का तर्क नहीं बदलता है, तो कॉल का परिणाम useCallbackस्मृति के उसी क्षेत्र को इंगित करेगा। हमारे उदाहरण में, इसका मतलब यह है कि जब एक ही समाचार को फिर से जोड़ा जाता है, तो प्रोप onPressघटक NewsItemBodyनहीं बदलेगा। हुक केवल कार्यात्मक घटकों में उपयोग किया जा सकता है, इसलिए घटक का अंतिम NewsItemरूप निम्नानुसार होगा:

function NewsItemComponent(props) {
  ...
  const {itemKey, onBodyPress} = props.item;
  const onPressBody = useCallback(() => onBodyPress(itemKey), [itemKey, onBodyPress]);
  return (
    <View>
      ...
      <NewsItemBody
        ...
        onPress={onPressBody}
        ...
      />
    </View>
  );
}

और लॉग:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_1
ITEM_TITLE_1

पूर्ण उदाहरण कोड

ऐरे और ऑब्जेक्ट


जावास्क्रिप्ट में, फ़ंक्शन को ऑब्जेक्ट्स के साथ, सरणियों के साथ दर्शाया जाता है। इसलिए, पिछले ब्लॉक से उदाहरण प्रॉप्स में एक नई वस्तु बनाने का एक विशेष मामला है। यह काफी सामान्य है, इसलिए मैंने इसे एक अलग पैराग्राफ में रखा।

प्रॉप्स में नए फ़ंक्शंस, सरणियों या ऑब्जेक्ट्स का कोई भी निर्माण एक घटक को पुन: रेंडरर की ओर ले जाता है। इस नियम पर निम्नलिखित उदाहरण में विचार करें। आइए NewsItemBodyदो मूल्यों की संयुक्त शैली में पास करें :

<NewsItemBody
  ...
  style={[styles.body, styles.item]}
  ...
/>

और फिर, लॉग अतिरिक्त घटक रेंडरर्स दिखाता है:

SCREEN
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1
--------------[ REFRESHING ]--------------
SCREEN
ITEM_3
ITEM_TITLE_3
ITEM_BODY_3
ITEM_2
ITEM_TITLE_2
ITEM_BODY_2
ITEM_1
ITEM_TITLE_1
ITEM_BODY_1

इस समस्या को हल करने के लिए, आप एक अलग शैली का चयन कर सकते हैं जो गठबंधन करेगा bodyऔर item, उदाहरण के लिए, सरणी की घोषणा [styles.body, styles.item]को एक वैश्विक चर में स्थानांतरित करेगा

पूर्ण उदाहरण कोड

एरे रिड्यूसर


उपयोग के साथ जुड़े "ब्रेक" के एक अन्य लोकप्रिय स्रोत पर विचार करें FlatListएक क्लासिक अनुप्रयोग जिसमें सर्वर से आइटमों की एक लंबी सूची होती है जो पृष्ठांकन लागू करता है। यही है, यह पहले पृष्ठ के रूप में तत्वों के एक सीमित सेट को लोड करता है, जब वर्तमान तत्वों की सूची समाप्त होती है, तो यह अगले पृष्ठ को लोड करता है, और इसी तरह। एक आइटम सूची reducer इस तरह लग सकता है:

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

जब प्रत्येक अगला पृष्ठ एप्लिकेशन की शैली में लोड होता है, तो पहचानकर्ताओं का एक नया सरणी बनाया जाता है। यदि हम इस सरणी को बाद में प्रॉप्स के लिए पास करते हैं FlatList, तो यहां घटक रेंडर लॉग्स कैसा दिखेगा:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..10>
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
ITEM_<1..30>

इस उदाहरण के लिए, मैंने परीक्षण एप्लिकेशन में कुछ बदलाव किए हैं।

  • पृष्ठ का आकार 10 समाचार पर सेट करें।
  • item NewsItem FlatList-, connect. NewsItem React.Component .
  • .
  • . №1 .

उदाहरण से पता चलता है कि जब प्रत्येक अगला पृष्ठ लोड होता है, तो सभी पुराने तत्वों को फिर से प्रदान किया जाता है, फिर पुराने तत्वों और नए पृष्ठ के तत्वों को फिर से प्रस्तुत किया जाता है। गणित प्रेमियों के लिए: यदि पृष्ठ का आकार बराबर है X, तो जब iith पेज लोड किया जाता है , तो केवल Xनए तत्वों को रेंडर करने के बजाय , तत्वों का प्रतिपादन किया जाता है (i - 1) * X + i * X

"ठीक है," आप कहते हैं, "मैं समझता हूं कि एक नया पृष्ठ जोड़ने के बाद सभी तत्वों को क्यों खींचा जाता है: reducer ने एक नया सरणी, स्मृति का एक नया क्षेत्र, सब लौटा दिया। लेकिन हमें नए तत्वों को जोड़ने से पहले पुरानी सूची को प्रस्तुत करने की आवश्यकता क्यों है? " "अच्छा सवाल है," मैं आपको जवाब दूंगा। यह VirtualizedListकिसके आधार पर घटक की स्थिति के साथ काम करने का एक परिणाम हैFlatListमैं विवरण में नहीं जाऊंगा, क्योंकि वे एक अलग लेख पर खींचते हैं। कौन परवाह करता है, मैं आपको प्रलेखन और स्रोत में तल्लीन करने की सलाह देता हूं

ऐसी गैर-इष्टतमता से कैसे छुटकारा पाएं? हम रिड्यूसर को फिर से लिखते हैं ताकि वह प्रत्येक पृष्ठ के लिए एक नया एरे न लौटे, लेकिन मौजूदा एक में तत्व जोड़ता है:

ध्यान! Antipattern!
. , , , PureComponent, . , . . Redux.

const newsIdList = (state = [], action) => {
  if (action.type === 'GOT_NEWS') {
    return action.news.map(item => item.key);
  } else if (action.type === 'GOT_OLDER_NEWS') {
    action.news.forEach(item => state.push(item.key));
    return state;
    // return [...state, ...action.news.map(item => item.key)];
  }
  return state;
};

उसके बाद, हमारा लॉग इस तरह दिखेगा:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<1..30>

हमने तत्वों को एक नए पृष्ठ पर जोड़ने से पहले पुराने तत्वों के प्रतिपादन से छुटकारा पा लिया , लेकिन पुराने तत्वों को सूची को अपडेट करने के बाद भी तैयार किया जाता है । अगले पृष्ठ के लिए रेंडरिंग की संख्या अब बराबर है i * X। सूत्र सरल हो गया है, लेकिन हम वहाँ नहीं रुकेंगे। हमारे पास केवल Xनए तत्व हैं, और हम केवल Xनए रेंडर चाहते हैं । हम पहले से ही परिचित ट्रिक्स का उपयोग उन समाचार रेंडरर्स को हटाने के लिए करेंगे जो प्रॉपर नहीं बदले हैं। वापसी से कनेक्ट करें NewsItem:

SCREEN
ITEM_<1..10>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<11..20>
--------------[ LOADING NEXT PAGE ]--------------
SCREEN
ITEM_<21..30>

ठीक! अब हम खुद से संतुष्ट हो सकते हैं। अनुकूलित करने के लिए कहीं नहीं है।

पूर्ण उदाहरण कोड

एक चौकस पाठक इंगित करेगा कि NewsItemलॉग से कनेक्ट करने के बाद , यह अंतिम उदाहरण में दिखेगा, चाहे आप रिड्यूसर को कैसे लागू करें। और यह सही होगा - यदि समाचार घटक रेंडर करने से पहले अपने प्रॉपर की जांच करता है, तो इससे कोई फर्क नहीं पड़ता कि पुराने एरे को reducer द्वारा उपयोग किया जाता है या यह एक नया बनाता है। केवल नए तत्व खींचे जाते हैं और केवल एक बार। हालांकि, एक नया बनाने के बजाय पुराने सरणी को बदलना हमें FlatListइसमें उपयोग किए गए घटक के अनावश्यक रेंडरिंग VirtualizedListऔर प्रॉपर तुल्यता जांच के अनावश्यक पुनरावृत्तियों से बचाता है NewsItem। बड़ी संख्या में तत्वों के साथ, यह प्रदर्शन में वृद्धि भी देता है।

Reducers में उत्परिवर्ती सरणियों और वस्तुओं का उपयोग करें अत्यधिक सावधानी के साथ होना चाहिए। इस उदाहरण में, यह उचित है, लेकिन यदि आपके पास, सामान्य कहने के लिए है PureComponent, तो जब आप तत्वों को उत्परिवर्तित सरणी में जोड़ते हैं, तो घटकों का प्रतिपादन नहीं किया जाएगा। वास्तव में इसके प्रॉप्स अपरिवर्तित रहते हैं, क्योंकि पहले और बाद में एक ही मेमोरी क्षेत्र में पॉइंट पॉइंट को अपडेट करने के बाद। इससे अप्रत्याशित परिणाम हो सकते हैं। कोई आश्चर्य नहीं कि वर्णित उदाहरण Redux के सिद्धांतों का उल्लंघन करता है

बाकी और कुछ...


यदि आप प्रस्तुति स्तर के पुस्तकालयों का उपयोग करते हैं, तो मैं आपको यह सुनिश्चित करने की सलाह देता हूं कि आप विस्तार से समझते हैं कि उन्हें कैसे लागू किया जाता है। हमारे आवेदन में, हम Swipeableपुस्तकालय से एक घटक का उपयोग करते हैं react-native-gesture-handlerयह आपको सूची से एक कार्ड स्वाइप करने पर अतिरिक्त कार्यों के ब्लॉक को लागू करने की अनुमति देता है।

कोड में, यह इस तरह दिखता है:

<Swipeable
  ...
  renderRightActions={this.renderRightActions}
  ...
>

विधि renderRightActionsया renderLeftActionsउस घटक को लौटाता है जो स्वाइप के बाद प्रदर्शित होता है। हमने आवश्यक सामग्री को फिट करने के लिए घटकों के परिवर्तन के दौरान पैनल की ऊंचाई निर्धारित और बदल दी। यह एक संसाधन-गहन प्रक्रिया है, लेकिन अगर यह स्वाइप एनीमेशन के दौरान होता है, तो उपयोगकर्ता को हस्तक्षेप दिखाई नहीं देता है।


समस्या यह है कि घटक मुख्य घटक प्रदान करने के समय Swipeableविधि renderRightActionsको कॉल करता है । सभी गणना और यहां तक ​​कि एक्शन बार का प्रतिपादन, जो कि स्वाइप से पहले दिखाई नहीं देता है, पहले से होता है। तो, इन सभी कार्यों को एक ही समय में सूची में सभी कार्डों के लिए किया जाता है। बोर्ड को स्क्रॉल करते समय यह महत्वपूर्ण "ब्रेक" का कारण बना।

समस्या को निम्नलिखित तरीके से हल किया गया था। यदि एक्शन पैनल मुख्य घटक के साथ मिलकर तैयार किया गया है, और स्वाइप के परिणामस्वरूप नहीं है, तो विधि मुख्य घटक renderRightActionsके Viewआकार को खाली कर देती है । अन्यथा, हम पहले की तरह अतिरिक्त क्रियाओं का पैनल बनाते हैं।
मैं यह उदाहरण देता हूं क्योंकि सहायक पुस्तकालय हमेशा आपकी अपेक्षा के अनुरूप काम नहीं करते हैं। और अगर ये प्रस्तुति-स्तरीय लाइब्रेरी हैं, तो यह सुनिश्चित करना बेहतर है कि वे अनावश्यक संसाधनों को बर्बाद नहीं कर रहे हैं।

जाँच - परिणाम


लेख में वर्णित समस्याओं को समाप्त करने के बाद, हमने रिएक्ट नेटिव पर एप्लिकेशन को काफी तेज कर दिया। अब इसे उसी तरह के प्रदर्शन से अलग करना मुश्किल है, जिसे मूल रूप से लागू किया गया है। अतिरिक्त रेंडरर्स ने अलग-अलग स्क्रीन के लोडिंग और उपयोगकर्ता की क्रियाओं की प्रतिक्रिया दोनों को धीमा कर दिया। सबसे अधिक, यह सूचियों पर ध्यान देने योग्य था, जहां दर्जनों घटकों को एक ही बार में खींचा जाता है। हमने सब कुछ अनुकूलित नहीं किया है, लेकिन एप्लिकेशन की मुख्य स्क्रीन अब धीमा नहीं होती हैं।

लेख के मुख्य बिंदु संक्षेप में नीचे सूचीबद्ध हैं।

  1. React Native : Props/State- .
  2. , React.PureComponent, , .
  3. , shouldComponentUpdate React.Memo .
  4. - . , (shallow compare). , .
  5. प्रस्तुति-स्तरीय पुस्तकालयों का समर्थन करने से संसाधनों की अप्रत्याशित बर्बादी हो सकती है। यह उनके आवेदन में सावधान रहने के लायक है।

बस इतना ही। मुझे आशा है कि आपको जानकारी उपयोगी लगी होगी। मैं किसी भी प्रतिक्रिया के लिए खुशी होगी!

उपयोगी सूत्र


  1. रिएक्ट + रेडक्स में रेंडरिंग को समझना
  2. जावास्क्रिप्ट में वस्तुओं की तुलना करना
  3. React.memo () का उपयोग करके प्रतिक्रियाशील कार्यात्मक घटकों में प्रदर्शन में सुधार
  4. Discord कैसे मूल निवासी iOS प्रदर्शन को प्रतिक्रियाशील मूल के साथ प्राप्त करता है

All Articles